[comp.sources.x] v11i073: xboard -- extension to XChess, Part01/03

dbs@decwrl.dec.com (02/15/91)

Submitted-by: dbs@decwrl.dec.com
Posting-number: Volume 11, Issue 73
Archive-name: xboard/part01

XBoard version 1.1

XBOARD is an X11/R4-based user interface for GNU Chess.  It uses the R4 Athena
widgets and Xt Intrinsics to provide an interactive referee for managing
a chess game between a user and a computer opponent or between two computers.
It manages a digital chess clock for each player and resets the clocks if time
control is achieved within a given number of moves.  A game can be started with
the initial chess position, with a series of moves from a game file or with a
position from a position file.  The "match" shell script runs a series of games
between two machines, alternating sides.  The man page xboard.1 describes the
features of xboard.

XBOARD was written by Dan Sears and Chris Sears.  XBOARD borrows its colors,
icon and piece bitmaps from xchess which was written and copyrighted by
Wayne Christopher.  We thank him for his work on XChess.

CAVEATS

XBOARD depends on the R4 Xt Intrinsics and R4 Athena Widget Set.  R3 won't do.

GNU Chess is available via anonymous FTP from, among other sites, gatekeeper.

    gatekeeper.dec.com    16.1.0.2    pub/GNU/gnuchess-3.1.tar.Z

It must be compiled with the -DCHESSTOOL option.

CHANGES

Version 1.1 -- Mon Jan  7 14:46:03 PST 1991

  - Fixed a bug in HandleUserMove() where the user could make a move while the
    machine was thinking.  The fix detects and ignores these moves.  onMove
    was not being used and was removed.

  - Substantially rewrote and simplified the clock code.  If a game was paused
    and then resumed, the clocks were out of sync.

  - Konstantinos Konstantinides added the -searchTime option.

  - Rewrote TwoMachinesPlay mode.

  - Rewrote DisplayClocks().

  - Hal Peterson contributed a new Imakefile.

  - Added a declaration, xtVersion, which will quickly break on R3 Intrinsics.

  - For people who don't like or use chess clocks a clockMode switch
    has been added.  It is on by default.  It can be turned off in the
    .Xdefaults file with

        XBoard.clockMode:    False

  - Detect if the visual doesn't support color.  If so, run in monoMode.
    An alternative would be to detect grayscale visual and use a collection
    of gray user interface colors.

  - Works with gcc now.  gcc complained about casting float constants
    as XtPointers.

  - Added keyboard equivalents.  Added an iconify keystroke, C or c.

  - Added StrStr() because many systems don't have this ANSI function.

  - Substantially rewrote and simplified the Two Machine code.

  - The bitmaps have been pushed into the bitmaps directory.

Tue Jan  8 16:23:22 PST 1991

  - Fixed a bug where a player could play after a game had been finished.

  - Fixed a bug where hint didn't work.  The local version of gnuchessr
    had been hacked.  The fix was to clone stderr and stdout for gnuchessr.

  - Kayvan Sylvan <satyr!kayvan@apple.com> contributed a patch for ESIX.
    It seems that select() on pipes is broken on his system.  We declined
    to incorporate his patch though, because it was a work-around for
    something that was broken on one system, and selfishly, that system
    was not my own.  Nevertheless, it is likely that other System V users
    on PC's may need to use this patch and it is is included as the file
    ESIX.patch.  To use it, type

        patch xboard.c ESIX.patch

  - Any button restarts a paused game.

  - Warning messages get displayed in the message box.

  - getlogin() does not work in all cases.
    It was replaced by getpwuid(getuid())->pw_name).

  - For systems with smaller screens, XBoard can use smaller pieces,
    and a smaller board.  -bigSizeMode False uses a smaller set of pieces.
    These are scaled versions of the large pieces.  They look ok but could
    be improved.

  - -iconic doesn't work properly.  If XBoard is opened iconic then
    iconifying it later with a keystroke doesn't work.  I think
    this is an Xt bug.

  - the remoteShell resource was added for HP-UX systems
    and other systems where the remoteShell isn't rsh.

  - older non-ANSI versions of Sun compilers complain vociferously.

  - Roger Dubar, Konstantinos Konstantinides, Wolfgang S. Rupprecht,
    Paul Scowen, Mvh Smidt and Kayvan Sylvan all helped immensely during
    beta-testing.


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  ./xboard.c
# Wrapped by dbs@dbsmax.pa.dec.com on Sat Jan 26 14:47:31 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f './xboard.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./xboard.c'\"
else
echo shar: Extracting \"'./xboard.c'\" \(68496 characters\)
sed "s/^X//" >'./xboard.c' <<'END_OF_FILE'
X/*
X * XBoard -- an Xt user interface for GNU Chess
X *
X * Dan Sears
X * Chris Sears
X *
X * XBoard borrows its colors, icon and piece bitmaps from XChess
X * which was written and is copyrighted by Wayne Christopher.
X *
X * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
X *
X * All Rights Reserved
X *
X * Permission to use, copy, modify, and distribute this software and its
X * documentation for any purpose and without fee is hereby granted,
X * provided that the above copyright notice appear in all copies and that
X * both that copyright notice and this permission notice appear in
X * supporting documentation, and that the name of Digital not be
X * used in advertising or publicity pertaining to distribution of the
X * software without specific, written prior permission.
X *
X * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
X * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
X * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
X * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
X * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
X * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
X * SOFTWARE.
X *
X * Revision 1.0 90/10/31
X *		Initial release.
X *
X * Revision 1.1 91/01/26
X *		Major bug fix release.
X */
X
X#include <stdio.h>
X#include <signal.h>
X#include <pwd.h>
X#include <X11/Intrinsic.h>
X#include <X11/Shell.h>
X#include <X11/Xaw/Form.h>
X#include <X11/Xaw/List.h>
X#include <X11/Xaw/Label.h>
X#include <X11/StringDefs.h>
X#include <X11/cursorfont.h>
X#include "xboard.h"
X
X#include "bitmaps/solid_pawn.bitmap"
X#include "bitmaps/solid_rook.bitmap"
X#include "bitmaps/solid_knight.bitmap"
X#include "bitmaps/solid_bishop.bitmap"
X#include "bitmaps/solid_queen.bitmap"
X#include "bitmaps/solid_king.bitmap"
X
X#include "bitmaps/outline_pawn.bitmap"
X#include "bitmaps/outline_rook.bitmap"
X#include "bitmaps/outline_knight.bitmap"
X#include "bitmaps/outline_bishop.bitmap"
X#include "bitmaps/outline_queen.bitmap"
X#include "bitmaps/outline_king.bitmap"
X
X#include "bitmaps/pawn_small.bitmap"
X#include "bitmaps/rook_small.bitmap"
X#include "bitmaps/knight_small.bitmap"
X#include "bitmaps/bishop_small.bitmap"
X#include "bitmaps/queen_small.bitmap"
X#include "bitmaps/king_small.bitmap"
X
X#include "bitmaps/pawn_small_outline.bitmap"
X#include "bitmaps/rook_small_outline.bitmap"
X#include "bitmaps/knight_small_outline.bitmap"
X#include "bitmaps/bishop_small_outline.bitmap"
X#include "bitmaps/queen_small_outline.bitmap"
X#include "bitmaps/king_small_outline.bitmap"
X
X#include "bitmaps/icon.bitmap"
X
X#ifdef __STDC__
void main(int argc, char **argv);
void CreateGCs(void);
void CreatePieces(void);
void ReadBitmap(String name, Pixmap *pm, char big_bits[], char small_bits[]);
void CreateGrid(void);
int EventToSquare(int x);
int CharToPiece(int c);
void DrawSquare(int row, int column, int piece);
void DrawPosition(Widget w, XExposeEvent *event);
void InitPosition(void);
void CopyBoard(Board to, Board from);
void SendCurrentBoard(FILE *fp);
void HandleUserMove(Widget w, XEvent *event);
void HandleMachineMove(char *message);
void ReadGameFile(void);
void MakeMove(int *move_type, int from_x, int from_y, int to_x, int to_y);
void InitChessProgram(char *host_name, char *program_name, int *pid,
X	FILE **to, FILE **from, XtIntervalId *xid);
void ShutdownChessPrograms(char *message);
void SelectCommand(Widget w, XtPointer client_data, XtPointer call_data);
void QuitProc(void);
int PlayFromGameFileProc(void);
void MachinePlaysBlackProc(void);
void ForwardProc(void);
void ResetProc(void);
int SetupPositionFromFileProc(void);
void MachinePlaysWhiteProc(void);
void BackwardProc(void);
void FlipProc(void);
void SaveGameProc(void);
void SwitchProc(void);
void ForceProc(void);
void HintProc(void);
void SavePositionProc(void);
void TwoMachinesPlayProc(void);
void PauseProc(void);
void Iconify(void);
void SendToProgram(char *message, FILE *fp);
void ReceiveFromProgram(FILE *fp);
void DisplayMessage(char *message);
void DisplayClocks(int clock_mode);
void DisplayTimerLabel(Widget w, char *color, time_t timer);
char *TimeString(time_t tm);
void CatchPipeSignal(void);
void Usage(void);
char *StrStr(char *string, char *match);
X#else
void main();
void CreateGCs();
void CreatePieces();
void ReadBitmap();
void CreateGrid();
int EventToSquare();
int CharToPiece();
void DrawSquare();
void DrawPosition();
void InitPosition();
void CopyBoard();
void SendCurrentBoard();
void HandleUserMove();
void HandleMachineMove();
void ReadGameFile();
void MakeMove();
void InitChessProgram();
void ShutdownChessPrograms();
void SelectCommand();
void QuitProc();
int PlayFromGameFileProc();
void MachinePlaysBlackProc();
void ForwardProc();
void ResetProc();
int SetupPositionFromFileProc();
void MachinePlaysWhiteProc();
void BackwardProc();
void FlipProc();
void SaveGameProc();
void SwitchProc();
void ForceProc();
void HintProc();
void SavePositionProc();
void TwoMachinesPlayProc();
void PauseProc();
void Iconify();
void SendToProgram();
void ReceiveFromProgram();
void DisplayMessage();
void DisplayClocks();
void DisplayTimerLabel();
char *TimeString();
void CatchPipeSignal();
void Usage();
char *StrStr();
X#endif
X
int xtVersion = XtSpecificationRelease;	/* XBoard depends on Xt R4 or higher */
int xScreen;
Display *xDisplay;
Window xBoardWindow;
GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
X	bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC;
Pixmap solidPawnBitmap, solidRookBitmap, solidKnightBitmap, solidBishopBitmap,
X	solidQueenBitmap, solidKingBitmap, outlinePawnBitmap, outlineRookBitmap,
X	outlineKnightBitmap, outlineBishopBitmap, outlineQueenBitmap,
X	outlineKingBitmap, iconPixmap;
Widget shellWidget, formWidget, boardWidget, commandsWidget, messageWidget,
X	whiteTimerWidget, blackTimerWidget, widgetList[5];
XXSegment gridSegments[(BOARD_SIZE + 1) * 2];
XXtIntervalId firstProgramXID = NULL, secondProgramXID = NULL,
X	readGameXID = NULL, timerXID = NULL;
XXFontStruct *labelFont;
X
XFILE *fromFirstProgFP, *toFirstProgFP, *fromSecondProgFP,
X	*toSecondProgFP, *gameFileFP;
int currentMove = 0, firstMove = True, forwardMostMove = 0, flipView = False,
X	forwardForce = False, gameMode = BeginningOfGame, matchMode = MatchFalse,
X	firstProgramPID = 0, secondProgramPID = 0, squareSize = BIG_SQUARE_SIZE,
X	fromX = -1, fromY = -1, twoProgramState = False,
X	lastGameMode = BeginningOfGame;
char moveList[MAX_MOVES][8];
time_t whiteTimeRemaining, blackTimeRemaining;
X
Board boards[MAX_MOVES], initialPosition = {
X	{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
X		WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
X	{ WhitePawn, WhitePawn, WhitePawn, WhitePawn,
X		WhitePawn, WhitePawn, WhitePawn, WhitePawn },
X	{ EmptySquare, EmptySquare, EmptySquare, EmptySquare,
X		EmptySquare, EmptySquare, EmptySquare, EmptySquare },
X	{ EmptySquare, EmptySquare, EmptySquare, EmptySquare,
X		EmptySquare, EmptySquare, EmptySquare, EmptySquare },
X	{ EmptySquare, EmptySquare, EmptySquare, EmptySquare,
X		EmptySquare, EmptySquare, EmptySquare, EmptySquare },
X	{ EmptySquare, EmptySquare, EmptySquare, EmptySquare,
X		EmptySquare, EmptySquare, EmptySquare, EmptySquare },
X	{ BlackPawn, BlackPawn, BlackPawn, BlackPawn,
X		BlackPawn, BlackPawn, BlackPawn, BlackPawn },
X	{ BlackRook, BlackKnight, BlackBishop, BlackQueen,
X		BlackKing, BlackBishop, BlackKnight, BlackRook }
X};
X
String buttonStrings[] = {
X	"Quit", "Play From File", "Machine Black", "Forward",
X	"Reset", "Setup From File", "Machine White", "Backward",
X	"Flip View", "Save Game", "Switch Sides", "Force Moves",
X	"Hint", "Save Position", "Two Machines", "Pause"
X};
X
Arg shellArgs[] = {
X	{ XtNwidth, 0 },
X	{ XtNheight, 0 },
X	{ XtNminWidth, 0 },
X	{ XtNminHeight, 0 },
X	{ XtNmaxWidth, 0 },
X	{ XtNmaxHeight, 0 }
X};
X
Arg boardArgs[] = {
X	{ XtNborderWidth, 0 },
X	{ XtNwidth, LINE_GAP + BOARD_SIZE * (BIG_SQUARE_SIZE + LINE_GAP) },
X	{ XtNheight, LINE_GAP + BOARD_SIZE * (BIG_SQUARE_SIZE + LINE_GAP) }
X};
X
Arg commandsArgs[] = {
X	{ XtNborderWidth, 0 },
X	{ XtNdefaultColumns, 4 },
X	{ XtNforceColumns, True },
X	{ XtNlist, (int) buttonStrings },
X	{ XtNnumberStrings, XtNumber(buttonStrings) }
X};
X
Arg messageArgs[] = {
X	{ XtNborderWidth, 0 },
X	{ XtNwidth, 500 },
X	{ XtNjustify, XtJustifyLeft }
X};
X
Arg timerArgs[] = {
X	{ XtNborderWidth, 0 },
X	{ XtNjustify, XtJustifyLeft }
X};
X
typedef struct {
X	Pixel whitePieceColor;
X	Pixel blackPieceColor;
X	Pixel lightSquareColor;
X	Pixel darkSquareColor;
X	int movesPerSession;
X	String initString;
X	String firstChessProgram;
X	String secondChessProgram;
X	String firstHost;
X	String secondHost;
X	String solidPawnBitmap;
X	String solidRookBitmap;
X	String solidBishopBitmap;
X	String solidKnightBitmap;
X	String solidQueenBitmap;
X	String solidKingBitmap;
X	String outlinePawnBitmap;
X	String outlineRookBitmap;
X	String outlineBishopBitmap;
X	String outlineKnightBitmap;
X	String outlineQueenBitmap;
X	String outlineKingBitmap;
X	String remoteShell;
X	float timeDelay;
X	int timeControl;
X	String saveGameFile;
X	String readGameFile;
X	String savePositionFile;
X	String readPositionFile;
X	String matchMode;
X	Boolean monoMode;
X	Boolean debugMode;
X	Boolean clockMode;
X	Boolean bigSizeMode;
X	int searchTime;
X} AppData, *AppDataPtr;
X
AppData appData;
X
XXtResource clientResources[] = {
X	{
X		"whitePieceColor", "whitePieceColor", XtRPixel, sizeof(Pixel),
X		XtOffset(AppDataPtr, whitePieceColor), XtRString, WHITE_PIECE_COLOR
X	}, {
X		"blackPieceColor", "blackPieceColor", XtRPixel, sizeof(Pixel),
X		XtOffset(AppDataPtr, blackPieceColor), XtRString, BLACK_PIECE_COLOR
X	}, {
X		"lightSquareColor", "lightSquareColor", XtRPixel, sizeof(Pixel),
X		XtOffset(AppDataPtr, lightSquareColor), XtRString, LIGHT_SQUARE_COLOR
X	}, {
X		"darkSquareColor", "darkSquareColor", XtRPixel, sizeof(Pixel),
X		XtOffset(AppDataPtr, darkSquareColor), XtRString, DARK_SQUARE_COLOR
X	}, {
X		"movesPerSession", "movesPerSession", XtRInt, sizeof(int),
X		XtOffset(AppDataPtr, movesPerSession), XtRImmediate,
X		(XtPointer) MOVES_PER_SESSION
X	}, {
X		"initString", "initString", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, initString), XtRString, INIT_STRING
X	}, {
X		"firstChessProgram", "firstChessProgram", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, firstChessProgram), XtRString, FIRST_CHESS_PROGRAM
X	}, {
X		"secondChessProgram", "secondChessProgram", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, secondChessProgram), XtRString,
X		SECOND_CHESS_PROGRAM
X	}, {
X		"firstHost", "firstHost", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, firstHost), XtRString, FIRST_HOST
X	}, {
X		"secondHost", "secondHost", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, secondHost), XtRString, SECOND_HOST
X	}, {
X		"solidPawnBitmap", "solidPawnBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, solidPawnBitmap), XtRString, SOLID_PAWN_BITMAP
X	}, {
X		"solidRookBitmap", "solidRookBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, solidRookBitmap), XtRString, SOLID_ROOK_BITMAP
X	}, {
X		"solidKnightBitmap", "solidKnightBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, solidKnightBitmap), XtRString, SOLID_KNIGHT_BITMAP
X	}, {
X		"solidBishopBitmap", "solidBishopBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, solidBishopBitmap), XtRString, SOLID_BISHOP_BITMAP
X	}, {
X		"solidQueenBitmap", "solidQueenBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, solidQueenBitmap), XtRString, SOLID_QUEEN_BITMAP
X	}, {
X		"solidKingBitmap", "solidKingBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, solidKingBitmap), XtRString, SOLID_KING_BITMAP
X	}, {
X		"outlinePawnBitmap", "outlinePawnBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, outlinePawnBitmap), XtRString, OUTLINE_PAWN_BITMAP
X	}, {
X		"outlineRookBitmap", "outlineRookBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, outlineRookBitmap), XtRString, OUTLINE_ROOK_BITMAP
X	}, {
X		"outlineKnightBitmap", "outlineKnightBitmap", XtRString,
X		sizeof(String), XtOffset(AppDataPtr, outlineKnightBitmap), XtRString,
X		OUTLINE_KNIGHT_BITMAP
X	}, {
X		"outlineBishopBitmap", "outlineBishopBitmap", XtRString,
X		sizeof(String), XtOffset(AppDataPtr, outlineBishopBitmap), XtRString,
X		OUTLINE_BISHOP_BITMAP
X	}, {
X		"outlineQueenBitmap", "outlineQueenBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, outlineQueenBitmap), XtRString,
X		OUTLINE_QUEEN_BITMAP
X	}, {
X		"outlineKingBitmap", "outlineKingBitmap", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, outlineKingBitmap), XtRString, OUTLINE_KING_BITMAP
X	}, {
X		"remoteShell", "remoteShell", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, remoteShell), XtRString, "rsh"
X	}, {
X		"timeDelay", "timeDelay", XtRFloat, sizeof(float),
X		XtOffset(AppDataPtr, timeDelay), XtRString, (XtPointer) TIME_DELAY
X	}, {
X		"timeControl", "timeControl", XtRInt, sizeof(int),
X		XtOffset(AppDataPtr, timeControl), XtRImmediate,
X		(XtPointer) TIME_CONTROL
X	}, {
X		"saveGameFile", "saveGameFile", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, saveGameFile), XtRString, SAVE_GAME_FILE
X	}, {
X		"readGameFile", "readGameFile", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, readGameFile), XtRString, READ_GAME_FILE
X	}, {
X		"savePositionFile", "savePositionFile", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, savePositionFile), XtRString, SAVE_POSITION_FILE
X	}, {
X		"readPositionFile", "readPositionFile", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, readPositionFile), XtRString, READ_POSITION_FILE
X	}, {
X		"matchMode", "matchMode", XtRString, sizeof(String),
X		XtOffset(AppDataPtr, matchMode), XtRString, MATCH_MODE
X	}, {
X		"monoMode", "monoMode", XtRBoolean, sizeof(Boolean),
X		XtOffset(AppDataPtr, monoMode), XtRImmediate, (XtPointer) False
X	}, {
X		"debugMode", "debugMode", XtRBoolean, sizeof(Boolean),
X		XtOffset(AppDataPtr, debugMode), XtRImmediate, (XtPointer) False
X	}, {
X		"clockMode", "clockMode", XtRBoolean, sizeof(Boolean),
X		XtOffset(AppDataPtr, clockMode), XtRImmediate, (XtPointer) True
X	}, {
X		"bigSizeMode", "bigSizeMode", XtRBoolean, sizeof(Boolean),
X		XtOffset(AppDataPtr, bigSizeMode), XtRImmediate, (XtPointer) True
X	}, {
X		"searchTime","searchTime", XtRInt, sizeof(int),
X		XtOffset(AppDataPtr, searchTime), XtRImmediate, (XtPointer) TIME_SEARCH
X	}
X};
X
Pixmap *pieceToSolid[] = {
X	&solidPawnBitmap, &solidRookBitmap, &solidKnightBitmap,
X	&solidBishopBitmap, &solidQueenBitmap, &solidKingBitmap,
X	&solidPawnBitmap, &solidRookBitmap, &solidKnightBitmap,
X	&solidBishopBitmap, &solidQueenBitmap, &solidKingBitmap
X};
X
Pixmap *pieceToOutline[] = {
X	&outlinePawnBitmap, &outlineRookBitmap, &outlineKnightBitmap,
X	&outlineBishopBitmap, &outlineQueenBitmap, &outlineKingBitmap,
X	&outlinePawnBitmap, &outlineRookBitmap, &outlineKnightBitmap,
X	&outlineBishopBitmap, &outlineQueenBitmap, &outlineKingBitmap
X};
X
char pieceToChar[] = {
X	'P', 'R', 'N', 'B', 'Q', 'K',
X	'p', 'r', 'n', 'b', 'q', 'k', '.'
X};
X
XXrmOptionDescRec shellOptions[] = {
X	{ "-whitePieceColor", "whitePieceColor", XrmoptionSepArg, NULL },
X	{ "-wpc", "whitePieceColor", XrmoptionSepArg, NULL },
X	{ "-blackPieceColor", "blackPieceColor", XrmoptionSepArg, NULL },
X	{ "-bpc", "blackPieceColor", XrmoptionSepArg, NULL },
X	{ "-lightSquareColor", "lightSquareColor", XrmoptionSepArg, NULL },
X	{ "-lsc", "lightSquareColor", XrmoptionSepArg, NULL },
X	{ "-darkSquareColor", "darkSquareColor", XrmoptionSepArg, NULL },
X	{ "-dsc", "darkSquareColor", XrmoptionSepArg, NULL },
X	{ "-movesPerSession", "movesPerSession", XrmoptionSepArg, NULL },
X	{ "-mps", "movesPerSession", XrmoptionSepArg, NULL },
X	{ "-initString", "initString", XrmoptionSepArg, NULL },
X	{ "-init", "initString", XrmoptionSepArg, NULL },
X	{ "-firstChessProgram", "firstChessProgram", XrmoptionSepArg, NULL },
X	{ "-fcp", "firstChessProgram", XrmoptionSepArg, NULL },
X	{ "-secondChessProgram", "secondChessProgram", XrmoptionSepArg, NULL },
X	{ "-scp", "secondChessProgram", XrmoptionSepArg, NULL },
X	{ "-firstHost", "firstHost", XrmoptionSepArg, NULL },
X	{ "-fh", "firstHost", XrmoptionSepArg, NULL },
X	{ "-secondHost", "secondHost", XrmoptionSepArg, NULL },
X	{ "-sh", "secondHost", XrmoptionSepArg, NULL },
X	{ "-solidPawnBitmap", "solidPawnBitmap", XrmoptionSepArg, NULL },
X	{ "-spb", "solidPawnBitmap", XrmoptionSepArg, NULL },
X	{ "-solidRookBitmap", "solidRookBitmap", XrmoptionSepArg, NULL },
X	{ "-srb", "solidRookBitmap", XrmoptionSepArg, NULL },
X	{ "-solidBishopBitmap", "solidBishopBitmap", XrmoptionSepArg, NULL },
X	{ "-sbb", "solidBishopBitmap", XrmoptionSepArg, NULL },
X	{ "-solidKnightBitmap", "solidKnightBitmap", XrmoptionSepArg, NULL },
X	{ "-skb", "solidKnightBitmap", XrmoptionSepArg, NULL },
X	{ "-solidQueenBitmap", "solidQueenBitmap", XrmoptionSepArg, NULL },
X	{ "-sqb", "solidQueenBitmap", XrmoptionSepArg, NULL },
X	{ "-solidKingBitmap", "solidKingBitmap", XrmoptionSepArg, NULL },
X	{ "-skb", "solidKingBitmap", XrmoptionSepArg, NULL },
X	{ "-outlinePawnBitmap", "outlinePawnBitmap", XrmoptionSepArg, NULL },
X	{ "-opb", "outlinePawnBitmap", XrmoptionSepArg, NULL },
X	{ "-outlineRookBitmap", "outlineRookBitmap", XrmoptionSepArg, NULL },
X	{ "-orb", "outlineRookBitmap", XrmoptionSepArg, NULL },
X	{ "-outlineBishopBitmap", "outlineBishopBitmap", XrmoptionSepArg, NULL },
X	{ "-obb", "outlineBishopBitmap", XrmoptionSepArg, NULL },
X	{ "-outlineKnightBitmap", "outlineKnightBitmap", XrmoptionSepArg, NULL },
X	{ "-okb", "outlineKnightBitmap", XrmoptionSepArg, NULL },
X	{ "-outlineQueenBitmap", "outlineQueenBitmap", XrmoptionSepArg, NULL },
X	{ "-oqb", "outlineQueenBitmap", XrmoptionSepArg, NULL },
X	{ "-outlineKingBitmap", "outlineKingBitmap", XrmoptionSepArg, NULL },
X	{ "-okb", "outlineKingBitmap", XrmoptionSepArg, NULL },
X	{ "-remoteShell", "remoteShell", XrmoptionSepArg, NULL },
X	{ "-rsh", "remoteShell", XrmoptionSepArg, NULL },
X	{ "-timeDelay", "timeDelay", XrmoptionSepArg, NULL },
X	{ "-td", "timeDelay", XrmoptionSepArg, NULL },
X	{ "-timeControl", "timeControl", XrmoptionSepArg, NULL },
X	{ "-tc", "timeControl", XrmoptionSepArg, NULL },
X	{ "-saveGameFile", "saveGameFile", XrmoptionSepArg, NULL },
X	{ "-sgf", "saveGameFile", XrmoptionSepArg, NULL },
X	{ "-readGameFile", "readGameFile", XrmoptionSepArg, NULL },
X	{ "-rgf", "readGameFile", XrmoptionSepArg, NULL },
X	{ "-savePositionFile", "savePositionFile", XrmoptionSepArg, NULL },
X	{ "-spf", "savePositionFile", XrmoptionSepArg, NULL },
X	{ "-readPositionFile", "readPositionFile", XrmoptionSepArg, NULL },
X	{ "-rpf", "readPositionFile", XrmoptionSepArg, NULL },
X	{ "-matchMode", "matchMode", XrmoptionSepArg, NULL },
X	{ "-mm", "matchMode", XrmoptionSepArg, NULL },
X	{ "-monoMode", "monoMode", XrmoptionSepArg, NULL },
X	{ "-mono", "monoMode", XrmoptionSepArg, NULL },
X	{ "-debugMode", "debugMode", XrmoptionSepArg, NULL },
X	{ "-debug", "debugMode", XrmoptionSepArg, NULL },
X	{ "-clockMode", "clockMode", XrmoptionSepArg, NULL },
X	{ "-clock", "clockMode", XrmoptionSepArg, NULL },
X	{ "-bigSizeMode", "bigSizeMode", XrmoptionSepArg, NULL },
X	{ "-big", "bigSizeMode", XrmoptionSepArg, NULL },
X	{ "-searchTime", "searchTime", XrmoptionSepArg, NULL },
X	{ "-st", "searchTime", XrmoptionSepArg, NULL }
X};
X
XXtActionsRec boardActions[] = {
X	{ "DrawPosition", DrawPosition },
X	{ "HandleUserMove", HandleUserMove },
X	{ "QuitProc", QuitProc },
X	{ "ForwardProc", ForwardProc },
X	{ "BackwardProc", BackwardProc },
X	{ "PauseProc", PauseProc },
X	{ "Iconify", Iconify }
X};
X
char translationsTable[] = "<Expose>: DrawPosition() \n \
X							<BtnDown>: HandleUserMove() \n \
X							<BtnUp>: HandleUserMove() \n \
X							<Key>q: QuitProc() \n \
X							<Key>Q: QuitProc() \n \
X							<Key>f: ForwardProc() \n \
X							<Key>F: ForwardProc() \n \
X							<Key>b: BackwardProc() \n \
X							<Key>B: BackwardProc() \n \
X							<Key>p: PauseProc() \n \
X							<Key>P: PauseProc() \n \
X							<Key>i: Iconify() \n \
X							<Key>I: Iconify() \n \
X							<Key>c: Iconify() \n \
X							<Key>C: Iconify()";
X
void
main(argc, argv)
X	int argc;
X	char **argv;
X{
X	XSetWindowAttributes window_attributes;
X	char buf[MSG_SIZ];
X	Arg args[3];
X	int length;
X
X	setbuf(stdout, NULL); setbuf(stderr, NULL);
X
X	shellWidget = XtInitialize(argv[0], "XBoard", shellOptions,
X		XtNumber(shellOptions), &argc, argv);
X
X	if (argc > 1)
X		Usage();
X
X	XtGetApplicationResources(shellWidget, &appData, clientResources,
X		XtNumber(clientResources), NULL, 0);
X
X	/*
X	 * Determine matchMode state -- poor man's resource converter
X	 */
X	if (strcmp(appData.matchMode, "Init") == 0)
X		matchMode = MatchInit;
X	else if (strcmp(appData.matchMode, "Position") == 0)
X		matchMode = MatchPosition;
X	else if (strcmp(appData.matchMode, "Opening") == 0)
X		matchMode = MatchOpening;
X	else if (strcmp(appData.matchMode, "False") == 0)
X		matchMode = MatchFalse;
X	else {
X		fprintf(stderr, "xboard: bad matchMode option %s\n", appData.matchMode);
X		Usage();
X	}
X
X	xDisplay = XtDisplay(shellWidget);
X	xScreen = DefaultScreen(xDisplay);
X
X	if (!appData.bigSizeMode) {
X		squareSize = SMALL_SQUARE_SIZE;
X		XtSetArg(boardArgs[1], XtNwidth,
X			LINE_GAP + BOARD_SIZE * (SMALL_SQUARE_SIZE + LINE_GAP));
X		XtSetArg(boardArgs[2], XtNheight,
X			LINE_GAP + BOARD_SIZE * (SMALL_SQUARE_SIZE + LINE_GAP));
X	}
X
X	/*
X	 * Detect if there are not enough colors are available and adapt.
X	 */
X	if (DefaultDepth(xDisplay, xScreen) <= 2)
X		appData.monoMode = True;
X
X	/*
X	 * widget hierarchy
X	 */
X	formWidget = XtCreateManagedWidget("form",
X		formWidgetClass, shellWidget, NULL, 0);
X
X		widgetList[0] = whiteTimerWidget = XtCreateWidget("white time:",
X			labelWidgetClass, formWidget, timerArgs, XtNumber(timerArgs));
X
X		widgetList[1] = blackTimerWidget = XtCreateWidget("black time:",
X			labelWidgetClass, formWidget, timerArgs, XtNumber(timerArgs));
X
X		widgetList[2] = messageWidget = XtCreateWidget("message",
X			labelWidgetClass, formWidget, messageArgs, XtNumber(messageArgs));
X
X		widgetList[3] = commandsWidget = XtCreateWidget("commands",
X			listWidgetClass, formWidget, commandsArgs, XtNumber(commandsArgs));
X
X		widgetList[4] = boardWidget = XtCreateWidget("board",
X			widgetClass, formWidget, boardArgs, XtNumber(boardArgs));
X
X	XtManageChildren(widgetList, XtNumber(widgetList));
X
X	/*
X	 * Calculate the width of the timer labels.
X	 */
X	XtSetArg(args[0], XtNfont, &labelFont);
X	XtGetValues(whiteTimerWidget, args, 1);
X	if (appData.clockMode) {
X		sprintf(buf, "White: %s ", TimeString(appData.timeControl * 60));
X		length = XTextWidth(labelFont, buf, strlen(buf) - 1);
X	} else
X		length = XTextWidth(labelFont, "White  ", 7);
X	XtSetArg(args[0], XtNwidth, length);
X	XtSetValues(whiteTimerWidget, args, 1);
X	XtSetValues(blackTimerWidget, args, 1);
X
X	/*
X	 * formWidget uses these constraints but they are stored in the children.
X	 */
X	XtSetArg(args[0], XtNfromHoriz, whiteTimerWidget);
X	XtSetValues(blackTimerWidget, args, 1);
X	XtSetArg(args[0], XtNfromVert, whiteTimerWidget);
X	XtSetArg(args[1], XtNbackground, XBlackPixel(xDisplay, xScreen));
X	XtSetArg(args[2], XtNforeground, XWhitePixel(xDisplay, xScreen));
X	XtSetValues(messageWidget, args, 3);
X	XtSetArg(args[0], XtNfromVert, messageWidget);
X	XtSetValues(commandsWidget, args, 1);
X	XtSetArg(args[0], XtNfromVert, commandsWidget);
X	XtSetValues(boardWidget, args, 1);
X
X	XtRealizeWidget(shellWidget);
X
X	xBoardWindow = XtWindow(boardWidget);
X
X	/*
X	 * Create an icon.
X	 */
X	iconPixmap = XCreateBitmapFromData(xDisplay, XtWindow(shellWidget),
X		icon_bits, icon_width, icon_height);
X	XtSetArg(args[0], XtNiconPixmap, iconPixmap);
X	XtSetValues(shellWidget, args, 1);
X
X	/*
X	 * Create a cursor for the board widget.
X	 */
X	window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
X	XChangeWindowAttributes(xDisplay, xBoardWindow,
X		CWCursor, &window_attributes);
X
X	/*
X	 * Inhibit shell resizing.
X	 */
X	XtGetValues(shellWidget, shellArgs, 2);
X	shellArgs[4].value = shellArgs[2].value = shellArgs[0].value;
X	shellArgs[5].value = shellArgs[3].value = shellArgs[1].value;
X	XtSetValues(shellWidget, &shellArgs[2], 4);
X
X	CreateGCs();
X	CreateGrid();
X	CreatePieces();
X
X	XtAddCallback(commandsWidget, XtNcallback, SelectCommand, NULL);
X	XtAddActions(boardActions, XtNumber(boardActions));
X	XtOverrideTranslations(boardWidget,
X		XtParseTranslationTable(translationsTable));
X
X	DisplayMessage("");
X
X	/*
X	 * If there is to be a machine match, set it up.
X	 */
X	if (matchMode != MatchFalse)
X		TwoMachinesPlayProc();
X	else
X		ResetProc();
X
X	XtMainLoop();
X}
X
void
CreateGCs()
X{
X	XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
X		| GCBackground | GCFunction | GCPlaneMask;
X	XGCValues gc_values;
X
X	gc_values.plane_mask = AllPlanes;
X	gc_values.line_width = LINE_GAP;
X	gc_values.line_style = LineSolid;
X	gc_values.function = GXcopy;
X
X	gc_values.foreground = XBlackPixel(xDisplay, xScreen);
X	gc_values.background = XBlackPixel(xDisplay, xScreen);
X	lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
X
X	if (appData.monoMode) {
X		gc_values.foreground = XWhitePixel(xDisplay, xScreen);
X		gc_values.background = XBlackPixel(xDisplay, xScreen);
X		lightSquareGC = wbPieceGC
X			= XtGetGC(shellWidget, value_mask, &gc_values);
X
X		gc_values.foreground = XBlackPixel(xDisplay, xScreen);
X		gc_values.background = XWhitePixel(xDisplay, xScreen);
X		darkSquareGC = bwPieceGC
X			= XtGetGC(shellWidget, value_mask, &gc_values);
X	} else {
X		gc_values.foreground = appData.lightSquareColor;
X		gc_values.background = appData.darkSquareColor;
X		lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
X
X		gc_values.foreground = appData.darkSquareColor;
X		gc_values.background = appData.lightSquareColor;
X		darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
X
X		gc_values.foreground = appData.whitePieceColor;
X		gc_values.background = appData.darkSquareColor;
X		wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
X
X		gc_values.foreground = appData.whitePieceColor;
X		gc_values.background = appData.lightSquareColor;
X		wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
X
X		gc_values.foreground = appData.blackPieceColor;
X		gc_values.background = appData.darkSquareColor;
X		bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
X
X		gc_values.foreground = appData.blackPieceColor;
X		gc_values.background = appData.lightSquareColor;
X		blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
X	}
X}
X
void
CreatePieces()
X{
X	XSynchronize(xDisplay, True);	/* Work-around for xlib/xt buffering bug */
X
X	ReadBitmap(appData.solidPawnBitmap, &solidPawnBitmap,
X		solid_pawn_bits, pawn_small_bits);
X	ReadBitmap(appData.solidRookBitmap, &solidRookBitmap,
X		solid_rook_bits, rook_small_bits);
X	ReadBitmap(appData.solidKnightBitmap, &solidKnightBitmap,
X		solid_knight_bits, knight_small_bits);
X	ReadBitmap(appData.solidBishopBitmap, &solidBishopBitmap,
X		solid_bishop_bits, bishop_small_bits);
X	ReadBitmap(appData.solidQueenBitmap, &solidQueenBitmap,
X		solid_queen_bits, queen_small_bits);
X	ReadBitmap(appData.solidKingBitmap, &solidKingBitmap,
X		solid_king_bits, king_small_bits);
X
X	if (appData.monoMode) {
X		ReadBitmap(appData.outlinePawnBitmap, &outlinePawnBitmap,
X			outline_pawn_bits, pawn_small_outline_bits);
X		ReadBitmap(appData.outlineRookBitmap, &outlineRookBitmap,
X			outline_rook_bits, rook_small_outline_bits);
X		ReadBitmap(appData.outlineKnightBitmap, &outlineKnightBitmap,
X			outline_knight_bits, knight_small_outline_bits);
X		ReadBitmap(appData.outlineBishopBitmap, &outlineBishopBitmap,
X			outline_bishop_bits, bishop_small_outline_bits);
X		ReadBitmap(appData.outlineQueenBitmap, &outlineQueenBitmap,
X			outline_queen_bits, queen_small_outline_bits);
X		ReadBitmap(appData.outlineKingBitmap, &outlineKingBitmap,
X			outline_king_bits, king_small_outline_bits);
X	}
X
X	XSynchronize(xDisplay, False);	/* Work-around for xlib/xt buffering bug */
X}
X
void
ReadBitmap(name, pm, big_bits, small_bits)
X	String name;
X	Pixmap *pm;
X	char big_bits[], small_bits[];
X{
X	int x_hot, y_hot;
X	u_int w, h;
X
X	if (XReadBitmapFile(xDisplay, xBoardWindow, name,
X		&w, &h, pm, &x_hot, &y_hot) != BitmapSuccess
X		|| w != squareSize || h != squareSize) {
X		if (appData.bigSizeMode)
X			*pm = XCreateBitmapFromData(xDisplay, xBoardWindow,
X				big_bits, squareSize, squareSize);
X		else *pm = XCreateBitmapFromData(xDisplay, xBoardWindow,
X			small_bits, squareSize, squareSize);
X	}
X}
X
void
CreateGrid()
X{
X	int i;
X
X	for (i = 0; i < BOARD_SIZE + 1; i++) {
X		gridSegments[i].x1 = 0;
X		gridSegments[i].y1 = LINE_GAP / 2 + (i * (squareSize + LINE_GAP));
X		gridSegments[i].x2 = LINE_GAP + BOARD_SIZE * (squareSize + LINE_GAP);
X		gridSegments[i].y2 = LINE_GAP / 2 + (i * (squareSize + LINE_GAP));
X		gridSegments[i + BOARD_SIZE + 1].x1 = LINE_GAP / 2
X			+ (i * (squareSize + LINE_GAP));
X		gridSegments[i + BOARD_SIZE + 1].y1 = 0;
X		gridSegments[i + BOARD_SIZE + 1].x2 = LINE_GAP / 2
X			+ (i * (squareSize + LINE_GAP));
X		gridSegments[i + BOARD_SIZE + 1].y2 =
X			BOARD_SIZE * (squareSize + LINE_GAP);
X	}
X}
X
X/*
X * If the user selects on a border boundary or off the board, return failure.
X * Otherwise map the event coordinate to the square.
X */
int
XEventToSquare(x)
X	int x;
X{
X	if (x < LINE_GAP)
X		return -1;
X	x -= LINE_GAP;
X	if ((x % (squareSize + LINE_GAP)) >= squareSize)
X		return -1;
X	x /= (squareSize + LINE_GAP);
X	if (x >= BOARD_SIZE)
X		return -1;
X	return x;
X}
X
int
CharToPiece(c)
X	int c;
X{
X	switch (c) {
X	default:
X	case '.':	return EmptySquare;
X	case 'P':	return WhitePawn;
X	case 'R':	return WhiteRook;
X	case 'N':	return WhiteKnight;
X	case 'B':	return WhiteBishop;
X	case 'Q':	return WhiteQueen;
X	case 'K':	return WhiteKing;
X	case 'p':	return BlackPawn;
X	case 'r':	return BlackRook;
X	case 'n':	return BlackKnight;
X	case 'b':	return BlackBishop;
X	case 'q':	return BlackQueen;
X	case 'k':	return BlackKing;
X	}
X}
X
void
DrawSquare(row, column, piece)
X	int row, column, piece;
X{
X	int square_color, x, y;
X
X	if (flipView)
X		column = (BOARD_SIZE - 1) - column;
X	else
X		row = (BOARD_SIZE - 1) - row;
X
X	square_color = ((column % 2 == 1) && (row % 2 == 1))
X		|| ((column % 2 == 0) && (row % 2 == 0));
X
X	x = LINE_GAP + column * (squareSize + LINE_GAP);
X	y = LINE_GAP + row * (squareSize + LINE_GAP);
X
X	if (piece == EmptySquare)
X		XFillRectangle(xDisplay, xBoardWindow, square_color
X			? lightSquareGC : darkSquareGC, x, y, squareSize, squareSize);
X	else if (appData.monoMode) {
X		if (square_color)
X			XCopyPlane(xDisplay, piece < BlackPawn
X				? *pieceToOutline[piece] : *pieceToSolid[piece], xBoardWindow,
X				bwPieceGC, 0, 0, squareSize, squareSize, x, y, 1);
X		else XCopyPlane(xDisplay, piece < BlackPawn
X				? *pieceToSolid[piece] : *pieceToOutline[piece], xBoardWindow,
X				wbPieceGC, 0, 0, squareSize, squareSize, x, y, 1);
X	} else {
X		if (square_color)
X			XCopyPlane(xDisplay, *pieceToSolid[piece],
X				xBoardWindow, piece < BlackPawn ? wlPieceGC : blPieceGC,
X				0, 0, squareSize, squareSize, x, y, 1);
X		else XCopyPlane(xDisplay, *pieceToSolid[piece],
X				xBoardWindow, piece < BlackPawn ? wdPieceGC : bdPieceGC,
X				0, 0, squareSize, squareSize, x, y, 1);
X	}
X}
X
X/*
X * event handler for redrawing the board
X */
void
DrawPosition(w, event)
X	Widget w;
X	XExposeEvent *event;
X{
X	Arg args[1];
X	int i, j;
X	Boolean iconic;
X
X	/*
X	 * We probably shouldn't have to do this, but R4 Xt gets confused.
X	 */
X	XtSetArg(args[0], XtNiconic, &iconic);
X	XtGetValues(shellWidget, args, 1);
X	if (iconic == False) {
X		XtSetArg(args[0], XtNiconic, False);
X		XtSetValues(shellWidget, args, 1);
X	}
X
X	/*
X	 * It would be simpler to clear the window with XClearWindow()
X	 * but this causes a very distracting flicker.
X	 */
X	XDrawSegments(xDisplay, xBoardWindow, lineGC,
X		gridSegments, (BOARD_SIZE + 1) * 2);
X
X	for (i = 0; i < BOARD_SIZE; i++)
X		for (j = 0; j < BOARD_SIZE; j++)
X			DrawSquare(i, j, (int) boards[currentMove][i][j]);
X
X	XSync(xDisplay, False);
X}
X
void
InitPosition()
X{
X	currentMove = forwardMostMove = 0;
X	CopyBoard(boards[0], initialPosition);
X	DrawPosition(boardWidget, (XExposeEvent *) NULL);
X}
X
void
CopyBoard(to, from)
X	Board to, from;
X{
X	int i, j;
X
X	for (i = 0; i < BOARD_SIZE; i++)
X		for (j = 0; j < BOARD_SIZE; j++)
X			to[i][j] = from[i][j];
X}
X
void
SendCurrentBoard(fp)
X	FILE *fp;
X{
X	char message[MSG_SIZ];
X	u_char *bp;
X	int i, j;
X
X	SendToProgram("edit\n", fp);
X	SendToProgram("#\n", fp);
X	for (i = BOARD_SIZE - 1; i >= 0; i--) {
X		bp = &boards[currentMove][i][0];
X		for (j = 0; j < BOARD_SIZE; j++, bp++) {
X			if (*bp < BlackPawn) {
X				sprintf(message, "%c%c%c\n", pieceToChar[*bp],
X					'a' + j, '1' + i);
X				SendToProgram(message, fp);
X			}
X		}
X	}
X
X	SendToProgram("c\n", fp);
X	for (i = BOARD_SIZE - 1; i >= 0; i--) {
X		bp = &boards[currentMove][i][0];
X		for (j = 0; j < BOARD_SIZE; j++, bp++) {
X			if ((*bp != EmptySquare) && (*bp >= BlackPawn)) {
X				sprintf(message, "%c%c%c\n", pieceToChar[*bp - BlackPawn],
X					'a' + j, '1' + i);
X				SendToProgram(message, fp);
X			}
X		}
X	}
X
X	SendToProgram(".\n", fp);
X}
X
X/*
X * event handler for parsing user moves
X */
void
HandleUserMove(w, event)
X	Widget w;
X	XEvent *event;
X{
X	int move_type, to_x, to_y;
X	char user_move[MSG_SIZ];
X
X	if ((w != boardWidget) || (matchMode != MatchFalse))
X		return;
X
X	switch (gameMode) {
X	case EndOfGame:
X	case PlayFromGameFile:
X		return;
X	case MachinePlaysWhite:
X		if ((currentMove % 2) == 0)
X			return;
X		break;
X	case MachinePlaysBlack:
X		if ((currentMove % 2) == 1)
X			return;
X		break;
X	default:
X		break;
X	}
X
X	switch (event->type) {
X	case ButtonPress:
X		if ((fromX >= 0) || (fromY >= 0))
X			return;
X		if ((fromX = EventToSquare(event->xbutton.x)) < 0)
X			break;
X		if ((fromY = EventToSquare(event->xbutton.y)) < 0)
X			break;
X		if (flipView)
X			fromX = BOARD_SIZE - 1 - fromX;
X		else
X			fromY = BOARD_SIZE - 1 - fromY;
X		break;
X	case ButtonRelease:
X		if ((fromX < 0) || (fromY < 0))
X			return;
X		if ((to_x = EventToSquare(event->xbutton.x)) < 0)
X			break;
X		if ((to_y = EventToSquare(event->xbutton.y)) < 0)
X			break;
X		if (flipView)
X			to_x = BOARD_SIZE - 1 - to_x;
X		else
X			to_y = BOARD_SIZE - 1 - to_y;
X
X		MakeMove(&move_type, fromX, fromY, to_x, to_y);
X
X		switch (move_type) {
X		case WhitePromotion:
X		case BlackPromotion:	/* GNU Chess limitation of only Q promotion */
X			sprintf(user_move, "%c%c%c%c(Q)\n", /* GNU Chess limitation */
X				'a' + fromX, '1' + fromY, 'a' + to_x, '1' + to_y);
X			break;
X		case NormalMove:
X		case WhiteKingSideCastle:
X		case WhiteQueenSideCastle:
X		case WhiteCapturesEnPassant:
X		case BlackKingSideCastle:
X		case BlackQueenSideCastle:
X		case BlackCapturesEnPassant:
X			sprintf(user_move, "%c%c%c%c\n",
X				'a' + fromX, '1' + fromY, 'a' + to_x, '1' + to_y);
X			break;
X		}
X
X		fromX = fromY = -1;
X
X		SendToProgram(user_move, toFirstProgFP);
X		strcpy(moveList[currentMove - 1], user_move);
X
X		switch (gameMode) {
X		case PauseGame:
X			PauseProc();	/* a user move restarts a paused game */
X			break;
X		case ForceMoves:
X			forwardForce = False;
X			break;
X		case BeginningOfGame:
X		case SetupPosition:
X			lastGameMode = gameMode = MachinePlaysBlack;
X		case MachinePlaysBlack:
X			/*
X			 * gnuchess prefers to be told that it is black when it is on move.
X			 */
X			if (firstMove) {
X				firstMove = False;
X				if (currentMove > 1)	/* If it is being forced */
X					SendToProgram("black\n", toFirstProgFP);
X			}
X		case MachinePlaysWhite:
X			break;
X		}
X		break;
X	}
X}
X
void
HandleMachineMove(message)
X	char *message;
X{
X	char machine_move[MSG_SIZ], buf1[MSG_SIZ], buf2[MSG_SIZ];
X	int i, j, move_type, from_x, from_y, to_x, to_y;
X
X	if (strncmp(message, "warning:", 8) == 0) {
X		DisplayMessage(message);
X		return;
X	}
X
X	/*
X	 * If either host fails, reset to using localhost for both.
X	 * Display an error message.
X	 */
X	if ((StrStr(message, "unknown host") != NULL)
X		|| (StrStr(message, "No remote directory") != NULL)
X		|| (StrStr(message, "not found") != NULL)) {
X		ShutdownChessPrograms("");
X		strcpy(appData.firstHost, "localhost");
X		strcpy(appData.secondHost, "localhost");
X		if (matchMode != MatchFalse)
X			TwoMachinesPlayProc();
X		else
X			ResetProc();
X		strcpy(buf1, "using localhost - ");
X		strcat(buf1, message);
X		DisplayMessage(buf1);
X		return;
X	}
X
X	/*
X	 * If the move is illegal, cancel it and redraw the board.
X	 */
X	if (strncmp(message, "Illegal move", 12) == 0) {
X		if (gameMode == PlayFromGameFile) {
X			lastGameMode = gameMode;
X			gameMode = BeginningOfGame;
X			sprintf(buf1, "Illegal move: %s", moveList[currentMove - 1]);
X			ShutdownChessPrograms(buf1);
X			return;
X		}
X
X		currentMove--;
X		DisplayClocks(DisplayTimers);
X		DisplayMessage(message);
X
X		/*
X		 * Only redraw the squares that have changed.
X		 */
X		for (i = 0; i < BOARD_SIZE; i++)
X			for (j = 0; j < BOARD_SIZE; j++)
X				if (boards[currentMove][i][j] != boards[currentMove + 1][i][j])
X					DrawSquare(i, j, (int) boards[currentMove][i][j]);
X
X		XSync(xDisplay, False);
X		return;
X	}
X
X	if (strncmp(message, "Hint:", 5) == 0) {
X		DisplayMessage(message);
X		return;
X	}
X
X	/*
X	 * win, lose or draw
X	 */
X	if (strncmp(message, "White", 5) == 0) {
X		ShutdownChessPrograms("White wins");
X		return;
X	} else if (strncmp(message, "Black", 5) == 0) {
X		ShutdownChessPrograms("Black wins");
X		return;
X	} else if (strncmp(message, "Draw", 4) == 0) {
X		ShutdownChessPrograms("Draw");
X		return;
X	}
X
X	/*
X	 * normal machine reply move
X	 */
X	if (StrStr(message, "...") != NULL) {
X		sscanf(message, "%s %s %s", buf1, buf2, machine_move);
X		if (machine_move[0] == '\0')
X			return;
X	} else
X		return; /* ignore noise */
X
X	strcpy(moveList[currentMove], machine_move);
X
X	from_x = machine_move[0] - 'a';
X	from_y = machine_move[1] - '1';
X	to_x = machine_move[2] - 'a';
X	to_y = machine_move[3] - '1';
X
X	MakeMove(&move_type, from_x, from_y, to_x, to_y);
X
X	switch (gameMode) {
X	case PauseGame:
X	case SetupPosition:
X		break;
X	case ForceMoves:
X	case PlayFromGameFile:
X		strncat(machine_move, "\n", 1);
X		SendToProgram(machine_move, toFirstProgFP);
X		break;
X	case TwoMachinesPlay:
X		strncat(machine_move, "\n", 1);
X		SendToProgram(machine_move, currentMove % 2
X			? toFirstProgFP : toSecondProgFP);
X		/*
X		 * gnuchess prefers to be told that it is black when it is on move.
X		 */
X		if (firstMove) {
X			firstMove = False;
X			SendToProgram("black\n", toFirstProgFP);
X		}
X		break;
X	}
X}
X
void
ReadGameFile()
X{
X	static char move_number[MSG_SIZ], white_move[MSG_SIZ], black_move[MSG_SIZ];
X	int move_type, from_x, from_y, to_x, to_y;
X	char move[MSG_SIZ];
X
X	if ((gameMode == EndOfGame) || (gameMode == BeginningOfGame)) {
X		fclose(gameFileFP);
X		gameFileFP = NULL;
X		return;
X	}
X
next_move:
X	if (currentMove % 2 == 0) {
next_line:
X		if (fgets(move, sizeof(move), gameFileFP) == (char *) NULL) {
X			lastGameMode = gameMode = MachinePlaysBlack;
end_parse:
X			if (readGameXID != NULL) {
X				XtRemoveTimeOut(readGameXID);
X				readGameXID = NULL;
X			}
X			fclose(gameFileFP);
X			gameFileFP = NULL;
X			return;
X		}
X
X		if ((move[0] == '%') || (move[0] == '!'))
X			goto next_line;
X
X		move_number[0] = white_move[0] = black_move[0] = NULL;
X
X		if (sscanf(move, "%s %s %s", move_number,
X			white_move, black_move) == EOF)
X			goto end_parse;
X
X		if (white_move[0] == '\0') {
X			if (move_number[0] != '\0')
X				strcpy(white_move, move_number);
X			else {
X				DisplayMessage("End of Game");
X				goto end_parse;
X			}
X		}
X
X		if ((strcmp(white_move, "draw") == 0)
X			|| (strcmp(white_move, "Draw") == 0)
X			|| (strcmp(white_move, "DRAW") == 0)
X			|| (strcmp(white_move, "Resign") == 0)
X			|| (strcmp(white_move, "resign") == 0)
X			|| (strcmp(white_move, "RESIGN") == 0)
X			|| (strcmp(white_move, "0-1") == 0)
X			|| (strcmp(white_move, "1-0") == 0)) {
X			DisplayMessage(white_move);
X			lastGameMode = gameMode;
X			gameMode = BeginningOfGame;
X			goto end_parse;
X		} if (strcmp(white_move, "o-o-o") == 0) {
X			from_x = 4;
X			from_y = 0;
X			to_x = 2;
X			to_y = 0;
X		} else if (strcmp(white_move, "o-o") == 0) {
X			from_x = 4;
X			from_y = 0;
X			to_x = 6;
X			to_y = 0;
X		} else {
X			from_x = white_move[0] - 'a';
X			from_y = white_move[1] - '1';
X			to_x = white_move[2] - 'a';
X			to_y = white_move[3] - '1';
X		}
X
X		strncat(white_move, "\n", 1);
X		SendToProgram(white_move, toFirstProgFP);
X		strcpy(moveList[currentMove], white_move);
X	} else {
X		if (black_move[0] == '\0') {
X			DisplayMessage("End of Game");
X			goto end_parse;
X		} if ((strcmp(black_move, "draw") == 0)
X			|| (strcmp(black_move, "Draw") == 0)
X			|| (strcmp(black_move, "DRAW") == 0)
X			|| (strcmp(black_move, "Resign") == 0)
X			|| (strcmp(black_move, "resign") == 0)
X			|| (strcmp(black_move, "RESIGN") == 0)
X			|| (strcmp(black_move, "0-1") == 0)
X			|| (strcmp(black_move, "1-0") == 0)) {
X			DisplayMessage(black_move);
X			lastGameMode = gameMode;
X			gameMode = BeginningOfGame;
X			goto end_parse;
X		} if (strcmp(black_move, "o-o-o") == 0) {
X			from_x = 4;
X			from_y = 7;
X			to_x = 2;
X			to_y = 7;
X		} else if (strcmp(black_move, "o-o") == 0) {
X			from_x = 4;
X			from_y = 7;
X			to_x = 6;
X			to_y = 7;
X		} else {
X			from_x = black_move[0] - 'a';
X			from_y = black_move[1] - '1';
X			to_x = black_move[2] - 'a';
X			to_y = black_move[3] - '1';
X		}
X
X		strncat(black_move, "\n", 1);
X		SendToProgram(black_move, toFirstProgFP);
X		strcpy(moveList[currentMove], black_move);
X	}
X
X	MakeMove(&move_type, from_x, from_y, to_x, to_y);
X
X	if (matchMode == MatchOpening)
X		goto next_move;
X
X	readGameXID = XtAddTimeOut((int) (1000 * appData.timeDelay),
X		ReadGameFile, NULL);
X}
X
X/*
X * MakeMove() displays moves.  If they are illegal, GNU chess will detect
X * this and send an Illegal move message.  XBoard will then retract the move.
X * The clockMode False case is tricky because it displays the player on move.
X */
void
MakeMove(move_type, from_x, from_y, to_x, to_y)
X	int *move_type, from_x, from_y, to_x, to_y;
X{
X	char message[MSG_SIZ];
X	int move;
X
X	if ((gameMode != PlayFromGameFile)
X		&& (gameMode != ForceMoves) && appData.clockMode)
X		DisplayClocks(DisplayTimers);
X
X	CopyBoard(boards[currentMove + 1], boards[currentMove]);
X	forwardMostMove = ++currentMove;
X
X	move = (currentMove + 1) / 2;
X
X	if (!appData.clockMode)
X		DisplayClocks(DisplayTimers);
X
X	if (from_y == 0 && from_x == 4				/* white king-side castle */
X			&& boards[currentMove][from_y][from_x] == WhiteKing
X			&& to_y == 0 && to_x == 6) {
X		*move_type = WhiteKingSideCastle;
X		boards[currentMove][0][7] = EmptySquare;
X		boards[currentMove][0][6] = WhiteKing;
X		boards[currentMove][0][5] = WhiteRook;
X		boards[currentMove][0][4] = EmptySquare;
X		DrawSquare(0, 7, (int) boards[currentMove][0][7]);
X		DrawSquare(0, 6, (int) boards[currentMove][0][6]);
X		DrawSquare(0, 5, (int) boards[currentMove][0][5]);
X		DrawSquare(0, 4, (int) boards[currentMove][0][4]);
X		sprintf(message, "%d. 0-0", move);
X	} else if (from_y == 0 && from_x == 4		/* white queen-side castle */
X			&& boards[currentMove][from_y][from_x] == WhiteKing
X			&& to_y == 0 && to_x == 2) {
X		*move_type = WhiteQueenSideCastle;
X		boards[currentMove][0][0] = EmptySquare;
X		boards[currentMove][0][2] = WhiteKing;
X		boards[currentMove][0][3] = WhiteRook;
X		boards[currentMove][0][4] = EmptySquare;
X		DrawSquare(0, 0, (int) boards[currentMove][0][0]);
X		DrawSquare(0, 2, (int) boards[currentMove][0][2]);
X		DrawSquare(0, 3, (int) boards[currentMove][0][3]);
X		DrawSquare(0, 4, (int) boards[currentMove][0][4]);
X		sprintf(message, "%d. 0-0-0", move);
X	} else if (from_y == 6						/* white pawn promotion */
X			&& boards[currentMove][from_y][from_x] == WhitePawn && to_y == 7) {
X		*move_type = WhitePromotion;
X		boards[currentMove][6][from_x] = EmptySquare;
X		boards[currentMove][7][to_x] = WhiteQueen;
X		DrawSquare(6, from_x, (int) boards[currentMove][6][from_x]);
X		DrawSquare(7, to_x, (int) boards[currentMove][7][to_x]);
X		sprintf(message, "%d. %c8(Q)", move, from_x + 'a');
X	} else if ((from_y == 4)					/* white captures en passant */
X			&& (to_x != from_x)
X			&& (boards[currentMove][from_y][from_x] == WhitePawn)
X			&& (boards[currentMove][to_y][to_x] == EmptySquare)) {
X		*move_type = WhiteCapturesEnPassant;
X		boards[currentMove][from_y][from_x] = EmptySquare;
X		boards[currentMove][to_y][to_x] = WhitePawn;
X		boards[currentMove][to_y - 1][to_x] = EmptySquare;
X		DrawSquare(from_y, from_x, (int) boards[currentMove][from_y][from_x]);
X		DrawSquare(to_y, to_x, (int) boards[currentMove][to_y][to_x]);
X		DrawSquare(to_y - 1, to_x, (int) boards[currentMove][to_y - 1][to_x]);
X		sprintf(message, "%d. %c%c ep", move, from_x + 'a', to_x + 'a');
X	} else if (from_y == 7 && from_x == 4		/* black king-side castle */
X			&& boards[currentMove][from_y][from_x] == BlackKing
X			&& to_y == 7 && to_x == 6) {
X		*move_type = BlackKingSideCastle;
X		boards[currentMove][7][4] = EmptySquare;
X		boards[currentMove][7][5] = BlackRook;
X		boards[currentMove][7][6] = BlackKing;
X		boards[currentMove][7][7] = EmptySquare;
X		DrawSquare(7, 7, (int) boards[currentMove][7][7]);
X		DrawSquare(7, 6, (int) boards[currentMove][7][6]);
X		DrawSquare(7, 5, (int) boards[currentMove][7][5]);
X		DrawSquare(7, 4, (int) boards[currentMove][7][4]);
X		sprintf(message, "%d. ... 0-0", move);
X	} else if (from_y == 7 && from_x == 4		/* black queen-side castle */
X			&& boards[currentMove][from_y][from_x] == BlackKing
X			&& to_y == 7 && to_x == 2) {
X		*move_type = BlackQueenSideCastle;
X		boards[currentMove][7][0] = EmptySquare;
X		boards[currentMove][7][2] = BlackKing;
X		boards[currentMove][7][3] = BlackRook;
X		boards[currentMove][7][4] = EmptySquare;
X		DrawSquare(7, 0, (int) boards[currentMove][7][0]);
X		DrawSquare(7, 2, (int) boards[currentMove][7][2]);
X		DrawSquare(7, 3, (int) boards[currentMove][7][3]);
X		DrawSquare(7, 4, (int) boards[currentMove][7][4]);
X		sprintf(message, "%d. ... 0-0-0", move);
X	} else if (from_y == 1						/* black pawn promotion */
X			&& boards[currentMove][from_y][from_x] == BlackPawn && to_y == 0) {
X		*move_type = BlackPromotion;
X		boards[currentMove][1][from_x] = EmptySquare;
X		boards[currentMove][0][to_x] = BlackQueen;
X		DrawSquare(1, from_x, (int) boards[currentMove][1][from_x]);
X		DrawSquare(0, to_x, (int) boards[currentMove][0][to_x]);
X		sprintf(message, "%d. ... %c8(Q)", move, from_x + 'a');
X	} else if ((from_y == 3)					/* black captures en passant */
X			&& (to_x != from_x)
X			&& (boards[currentMove][from_y][from_x] == BlackPawn)
X			&& (boards[currentMove][to_y][to_x] == EmptySquare)) {
X		*move_type = BlackCapturesEnPassant;
X		boards[currentMove][from_y][from_x] = EmptySquare;
X		boards[currentMove][to_y][to_x] = BlackPawn;
X		boards[currentMove][to_y + 1][to_x] = EmptySquare;
X		DrawSquare(from_y, from_x, (int) boards[currentMove][from_y][from_x]);
X		DrawSquare(to_y, to_x, (int) boards[currentMove][to_y][to_x]);
X		DrawSquare(to_y + 1, to_x, (int) boards[currentMove][to_y + 1][to_x]);
X		sprintf(message, "%d. ... %c%c ep", move, from_x + 'a', to_x + 'a');
X	} else {
X		*move_type = NormalMove;
X		boards[currentMove][to_y][to_x] = boards[currentMove][from_y][from_x];
X		boards[currentMove][from_y][from_x] = EmptySquare;
X		DrawSquare(to_y, to_x, (int) boards[currentMove][to_y][to_x]);
X		DrawSquare(from_y, from_x, (int) boards[currentMove][from_y][from_x]);
X		sprintf(message, "%d. %s%c%c%c%c", move, currentMove % 2 ?
X			"" : "... ", from_x + 'a', from_y + '1', to_x + 'a', to_y + '1');
X	}
X
X	DisplayMessage(message);
X	XSync(xDisplay, False);
X}
X
void
InitChessProgram(host_name, program_name, pid, to, from, xid)
X	char *host_name, *program_name;
X	int *pid;
X	FILE **to, **from;
X	XtIntervalId *xid;
X{
X	char time_control[10], moves_per_session[10], time_search[10];
X	int to_prog[2], from_prog[2];
X	FILE *from_fp, *to_fp;
X
X	signal(SIGPIPE, CatchPipeSignal);
X	pipe(to_prog);
X	pipe(from_prog);
X
X	if ((*pid = fork()) == 0) {
X		signal(SIGPIPE, CatchPipeSignal);
X		dup2(to_prog[0], 0);
X		dup2(from_prog[1], 1);
X		close(to_prog[0]);
X		close(to_prog[1]);
X		close(from_prog[0]);
X		close(from_prog[1]);
X		dup2(1, fileno(stderr));	/* force stderr to the pipe */
X		sprintf(time_control, "%d", appData.timeControl);
X		sprintf(time_search, "%d", appData.searchTime);
X		sprintf(moves_per_session, "%d", appData.movesPerSession);
X		if (strcmp(host_name, "localhost") == 0) {
X			if (appData.searchTime <= 0)
X				execlp(program_name, program_name,
X					moves_per_session, time_control, (char *) NULL);
X			else
X				execlp(program_name, program_name, time_search, (char *) NULL);
X		} else {
X			if (appData.searchTime <= 0)
X				execlp(appData.remoteShell, appData.remoteShell,
X					host_name, program_name, moves_per_session,
X					time_control, (char *) NULL);
X			else
X				execlp(appData.remoteShell, appData.remoteShell,
X					host_name, program_name, time_search, (char *) NULL);
X		}
X
X		perror(program_name);
X		exit(1);
X	}
X
X	close(to_prog[0]);
X	close(from_prog[1]);
X
X	*from = from_fp = fdopen(from_prog[0], "r");
X	*to = to_fp = fdopen(to_prog[1], "w");
X	setbuf(from_fp, NULL); setbuf(to_fp, NULL);
X
X	*xid = XtAddInput(fileno(from_fp), XtInputReadMask,
X		ReceiveFromProgram, from_fp);
X
X	SendToProgram(appData.initString, to_fp);
X}
X
void
ShutdownChessPrograms(message)
X	char *message;
X{
X	lastGameMode = gameMode;
X	gameMode = EndOfGame;
X	DisplayMessage(message);
X
X	if (firstProgramPID != 0) {
X		fclose(fromFirstProgFP);
X		fclose(toFirstProgFP);
X		fromFirstProgFP = toFirstProgFP = NULL;
X		kill(firstProgramPID, SIGTERM);
X	}
X
X	if (secondProgramPID != 0) {
X		fclose(fromSecondProgFP);
X		fclose(toSecondProgFP);
X		fromSecondProgFP = toSecondProgFP = NULL;
X		kill(secondProgramPID, SIGTERM);
X	}
X
X	if (firstProgramXID != NULL)
X		XtRemoveInput(firstProgramXID);
X	if (secondProgramXID != NULL)
X		XtRemoveInput(secondProgramXID);
X	if (readGameXID != NULL)
X		XtRemoveTimeOut(readGameXID);
X
X	firstProgramXID = secondProgramXID = readGameXID = NULL;
X	firstProgramPID = secondProgramPID = 0;
X
X	DisplayClocks(StopTimers);
X}
X
void
SelectCommand(w, client_data, call_data)
X	Widget w;
X	XtPointer client_data, call_data;
X{
X	XawListReturnStruct *list_return = XawListShowCurrent(w);
X
X	fromX = fromY = -1;
X
X	if ((gameMode == PauseGame) && (list_return->list_index != ButtonPause))
X		PauseProc();
X
X	switch (list_return->list_index) {
X	case ButtonQuit:
X		QuitProc();
X		break;
X	case ButtonBackward:
X		BackwardProc();
X		break;
X	case ButtonForward:
X		ForwardProc();
X		break;
X	case ButtonFlip:
X		FlipProc();
X		break;
X	case ButtonReset:
X		ResetProc();
X		break;
X	case ButtonSaveGame:
X		SaveGameProc();
X		break;
X	case ButtonSavePosition:
X		SavePositionProc();
X		break;
X	case ButtonHint:
X		HintProc();
X		break;
X	case ButtonSwitch:
X		SwitchProc();
X		break;
X	case ButtonSetupFromFile:
X		(void) SetupPositionFromFileProc();
X		break;
X	case ButtonPlayFromFile:
X		(void) PlayFromGameFileProc();
X		break;
X	case ButtonMachinePlaysBlack:
X		MachinePlaysBlackProc();
X		break;
X	case ButtonMachinePlaysWhite:
X		MachinePlaysWhiteProc();
X		break;
X	case ButtonTwoMachinesPlay:
X		TwoMachinesPlayProc();
X		break;
X	case ButtonForce:
X		ForceProc();
X		break;
X	case ButtonPause:
X		PauseProc();
X		break;
X	}
X
X	XawListUnhighlight(w);
X}
X
X/*
X * Button procedures in order.
X */
void
QuitProc()
X{
X	ShutdownChessPrograms("Quitting");
X	exit(0);
X}
X
int
PlayFromGameFileProc()
X{
X	char buf[MSG_SIZ];
X
X	if (gameMode != BeginningOfGame)
X		return False;
X
X	if ((gameFileFP = fopen(appData.readGameFile, "r")) == NULL) {
X		sprintf(buf, "Can't open %s", appData.readGameFile);
X		DisplayMessage(buf);
X		return False;
X	}
X
X	lastGameMode = gameMode;
X	gameMode = PlayFromGameFile;
X	InitPosition();
X	DisplayClocks(StopTimers);
X	SendToProgram(appData.initString, toFirstProgFP);
X	SendToProgram("force\n", toFirstProgFP);
X
X	/*
X	 * skip header information in game record file
X	 */
X	fgets(buf, MSG_SIZ, gameFileFP);
X	fgets(buf, MSG_SIZ, gameFileFP);
X	fgets(buf, MSG_SIZ, gameFileFP);
X
X	ReadGameFile();
X
X	return True;
X}
X
void
MachinePlaysBlackProc()
X{
X	if ((gameMode == EndOfGame) || (gameMode == PlayFromGameFile)
X		|| (matchMode != MatchFalse) || ((currentMove % 2) == 0))
X		return;
X
X	lastGameMode = gameMode = MachinePlaysBlack;
X	SendToProgram("black\n", toFirstProgFP);
X}
X
void
XForwardProc()
X{
X	char buf[MSG_SIZ];
X	int i, j;
X
X	if ((gameMode == EndOfGame) || (gameMode == PlayFromGameFile)
X			|| (matchMode != MatchFalse) || (currentMove >= forwardMostMove)
X			|| ((currentMove == 0) && (forwardMostMove == 0)))
X		return;
X
X	if (forwardForce == False) {
X		forwardForce = True;
X		SendToProgram("force\n", toFirstProgFP);
X	}
X
X	for (i = 0; i < BOARD_SIZE; i++)
X		for (j = 0; j < BOARD_SIZE; j++)
X			if (boards[currentMove + 1][i][j] != boards[currentMove][i][j])
X				DrawSquare(i, j, (int) boards[currentMove + 1][i][j]);
X
X	strcpy(buf, moveList[currentMove++]);
X	strncat(buf, "\n", 1);
X	SendToProgram(buf, toFirstProgFP);
X}
X
void
ResetProc()
X{
X	twoProgramState = flipView = forwardForce = False;
X	matchMode = MatchFalse;
X	firstMove = True;
X
X	ShutdownChessPrograms("");
X	lastGameMode = gameMode = BeginningOfGame;
X	InitPosition();
X	InitChessProgram(appData.firstHost, appData.firstChessProgram,
X		&firstProgramPID, &toFirstProgFP, &fromFirstProgFP, &firstProgramXID);
X	DisplayClocks(ResetTimers);
X}
X
int
SetupPositionFromFileProc()
X{
X	char *p, line[MSG_SIZ], buf[MSG_SIZ];
X	Board initial_position;
X	FILE *position_file_fp;
X	int i, j;
X
X	if (gameMode != BeginningOfGame)
X		return False;
X
X	if ((position_file_fp = fopen(appData.readPositionFile, "r")) == NULL) {
X		sprintf(buf, "Can't open %s", appData.readPositionFile);
X		ShutdownChessPrograms(buf);
X		lastGameMode = gameMode = BeginningOfGame;
X		matchMode = MatchFalse;
X		InitPosition();
X		return False;
X	}
X
X	lastGameMode = gameMode = SetupPosition;
X
X	/*
X	 * skip header information in position file
X	 */
X	fgets(line, MSG_SIZ, position_file_fp);
X	fgets(line, MSG_SIZ, position_file_fp);
X	fgets(line, MSG_SIZ, position_file_fp);
X
X	for (i = BOARD_SIZE - 1; i >= 0; i--) {
X		fgets(line, MSG_SIZ, position_file_fp);
X		for (p = line, j = 0; j < BOARD_SIZE; p++) {
X			if (*p == ' ')
X				continue;
X			initial_position[i][j++] = CharToPiece(*p);
X		}
X	}
X	fclose(position_file_fp);
X
X	currentMove = forwardMostMove = 0;
X	CopyBoard(boards[0], initial_position);
X	SendCurrentBoard(toFirstProgFP);
X	DrawPosition(boardWidget, (XExposeEvent *) NULL);
X
X	return True;
X}
X
void
MachinePlaysWhiteProc()
X{
X	if ((gameMode == EndOfGame) || (gameMode == PlayFromGameFile)
X		|| (matchMode != MatchFalse) || ((currentMove % 2) == 1))
X		return;
X
X	lastGameMode = gameMode = MachinePlaysWhite;
X	SendToProgram("white\n", toFirstProgFP);
X}
X
void
BackwardProc()
X{
X	int i, j;
X
X	if ((gameMode == EndOfGame) || (gameMode == PlayFromGameFile)
X		|| (currentMove <= 0) || (matchMode != MatchFalse))
X		return;
X
X	SendToProgram("undo\n", toFirstProgFP);
X	currentMove--;
X
X	for (i = 0; i < BOARD_SIZE; i++)
X		for (j = 0; j < BOARD_SIZE; j++)
X			if (boards[currentMove][i][j] != boards[currentMove + 1][i][j])
X				DrawSquare(i, j, (int) boards[currentMove][i][j]);
X}
X
void
XFlipProc()
X{
X	flipView = !flipView;
X	DrawPosition(boardWidget, (XExposeEvent *) NULL);
X}
X
void
SaveGameProc()
X{
X	char buf[MSG_SIZ], white_move[MSG_SIZ], black_move[MSG_SIZ];
X	int i = 0, len, move = 0;
X	time_t tm;
X
X	if ((gameFileFP = fopen(appData.saveGameFile, "w")) == NULL) {
X		sprintf(buf, "Can't open %s", appData.saveGameFile);
X		DisplayMessage(buf);
X		return;
X	}
X
X	tm = time((time_t *) NULL);
X	gethostname(buf, MSG_SIZ);
X
X	fprintf(gameFileFP, "xboard game file -- %s", ctime(&tm));
X	switch (lastGameMode) {
X	case MachinePlaysWhite:
X		fprintf(gameFileFP, "\t%s@%s vs. %s@%s\n", appData.firstChessProgram,
X			appData.firstHost, getpwuid(getuid())->pw_name, buf);
X		break;
X	case MachinePlaysBlack:
X		fprintf(gameFileFP, "\t%s@%s vs. %s@%s\n", getpwuid(getuid())->pw_name,
X			buf, appData.firstChessProgram, appData.firstHost);
X		break;
X	case TwoMachinesPlay:
X		fprintf(gameFileFP, "\t%s@%s vs. %s@%s\n",
X			appData.secondChessProgram, appData.secondHost,
X			appData.firstChessProgram, appData.firstHost);
X		break;
X	default:
X		fprintf(gameFileFP, "\n");
X		break;
X	}
X	fprintf(gameFileFP, "\talgebraic\n");
X
X	for (;;) {
X		if ((len = strlen(moveList[i])) == 0)
X			break;
X		/*
X		 * get rid of '\n' added to send the move to GNU Chess
X		 */
X		strcpy(white_move, moveList[i++]);
X		if (white_move[len - 1] == '\n')
X			white_move[len - 1] = NULL;
X		fprintf(gameFileFP, "%d. %s ", ++move, white_move);
X
X		if ((len = strlen(moveList[i])) == 0) {
X			fprintf(gameFileFP, "\n");
X			break;
X		}
X		strcpy(black_move, moveList[i++]);
X		if (black_move[len - 1] == '\n')
X			black_move[len - 1] = NULL;
X		fprintf(gameFileFP, "%s\n", black_move);
X	}
X	fclose(gameFileFP);
X	gameFileFP = NULL;
X}
X
void
SwitchProc()
X{
X	switch (gameMode) {
X	default:
X		return;
X	case MachinePlaysWhite:
X		lastGameMode = gameMode = MachinePlaysBlack;
X		break;
X	case MachinePlaysBlack:
X		if (currentMove == 0) {
X			MachinePlaysWhiteProc();
X			return;
X		}
X		lastGameMode = gameMode = MachinePlaysWhite;
X		break;
X	}
X
X	SendToProgram("switch\n", toFirstProgFP);
X}
X
void
XForceProc()
X{
X	if ((gameMode == EndOfGame) || (gameMode == PlayFromGameFile))
X		return;
X
X	switch (gameMode) {
X	case MachinePlaysWhite:
X		if ((currentMove % 2) == 0) {
X			DisplayMessage("Wait until your move");
X			return;
X		}
X		break;
X	case MachinePlaysBlack:
X		if ((currentMove % 2) == 1) {
X			DisplayMessage("Wait until your move");
X			return;
X		}
X	case BeginningOfGame:
X		break;
X	default:
X		return;
X	}
X
X	lastGameMode = gameMode = ForceMoves;
X	DisplayClocks(StopTimers);
X	SendToProgram("force\n", toFirstProgFP);
X}
X
void
HintProc()
X{
X	switch (gameMode) {
X	case MachinePlaysWhite:
X	case MachinePlaysBlack:
X	case BeginningOfGame:
X		SendToProgram("hint\n", toFirstProgFP);
X	default:
X		return;
X	}
X}
X
void
SavePositionProc()
X{
X	char buf[MSG_SIZ], host_name[MSG_SIZ];
X	FILE *position_file_fp;
X	time_t tm;
X	int i, j;
X
X	if ((position_file_fp = fopen(appData.savePositionFile, "w")) == NULL) {
X		sprintf(buf, "Can't open %s", appData.savePositionFile);
X		DisplayMessage(buf);
X		return;
X	}
X
X	tm = time((time_t *) NULL);
X	gethostname(host_name, MSG_SIZ);
X
X	fprintf(position_file_fp, "xboard position file -- %s", ctime(&tm));
X	switch (gameMode) {
X	case MachinePlaysWhite:
X		fprintf(position_file_fp, "\t%s@%s vs. %s@%s\n",
X			appData.firstChessProgram, appData.firstHost,
X			getpwuid(getuid())->pw_name, host_name);
X		break;
X	case MachinePlaysBlack:
X		fprintf(position_file_fp, "\t%s@%s vs. %s@%s\n",
X			getpwuid(getuid())->pw_name, host_name,
X			appData.firstChessProgram, appData.firstHost);
X		break;
X	case TwoMachinesPlay:
X		fprintf(position_file_fp, "\t%s@%s vs. %s@%s\n",
X			appData.secondChessProgram, appData.secondHost,
X			appData.firstChessProgram, appData.firstHost);
X		break;
X	default:
X		fprintf(position_file_fp, "\n");
X		break;
X	}
X	fprintf(position_file_fp, "\n");
X
X	for (i = BOARD_SIZE - 1; i >= 0; i--) {
X		for (j = 0; j < BOARD_SIZE; j++) {
X			fprintf(position_file_fp, "%c",
X				pieceToChar[boards[currentMove][i][j]]);
X			fputc(j == BOARD_SIZE - 1 ? '\n' : ' ', position_file_fp);
X		}
X	}
X
X	fclose(position_file_fp);
X}
X
void
TwoMachinesPlayProc()
X{
X	int i;
X
X	if ((gameMode == EndOfGame) || (twoProgramState == True))
X		return;
X
X	if (currentMove % 2) {
X		DisplayMessage("White must be on move");
X		return;
X	}
X
X	twoProgramState = True;
X
X	if (matchMode == MatchFalse) {
X		switch (gameMode) {
X		case MachinePlaysWhite:
X		case MachinePlaysBlack:
X		case PauseGame:
X		case TwoMachinesPlay:
X		case PlayFromGameFile:
X			return;
X		case ForceMoves:
X			matchMode = MatchOpening;
X			break;
X		case SetupPosition:
X			matchMode = MatchPosition;
X			break;
X		case BeginningOfGame:
X		default:
X			matchMode = MatchInit;
X			break;
X		}
X	}
X
X	flipView = forwardForce = False;
X	firstMove = False;
X	DisplayClocks(ResetTimers);
X	DisplayClocks(ReStartTimers);
X
X	switch (matchMode) {
X	case MatchOpening:
X		if (firstProgramXID == NULL) {
X			InitChessProgram(appData.firstHost, appData.firstChessProgram,
X				&firstProgramPID, &toFirstProgFP, &fromFirstProgFP,
X				&firstProgramXID);
X			if (!PlayFromGameFileProc()) {
X				ShutdownChessPrograms("Bad game file");
X				return;
X			}
X			DrawPosition(boardWidget, (XExposeEvent *) NULL);
X		}
X		InitChessProgram(appData.secondHost, appData.secondChessProgram,
X			&secondProgramPID, &toSecondProgFP, &fromSecondProgFP,
X			&secondProgramXID);
X		SendToProgram("force\n", toSecondProgFP);
X		for (i = 0; i < currentMove; i++)
X			SendToProgram(moveList[i], toSecondProgFP);
X		lastGameMode = gameMode = TwoMachinesPlay;
X		firstMove = True;
X		SendToProgram("white\n", toSecondProgFP);
X		break;
X	case MatchPosition:
X		if (firstProgramXID == NULL) {
X			InitChessProgram(appData.firstHost, appData.firstChessProgram,
X				&firstProgramPID, &toFirstProgFP, &fromFirstProgFP,
X				&firstProgramXID);
X			if (!SetupPositionFromFileProc())
X				return;
X		}
X		InitChessProgram(appData.secondHost, appData.secondChessProgram,
X			&secondProgramPID, &toSecondProgFP, &fromSecondProgFP,
X			&secondProgramXID);
X		SendCurrentBoard(toSecondProgFP);
X		lastGameMode = gameMode = TwoMachinesPlay;
X		firstMove = True;
X		SendToProgram("white\n", toSecondProgFP);
X		break;
X	case MatchInit:
X		InitPosition();
X		if (firstProgramXID == NULL)
X			InitChessProgram(appData.firstHost, appData.firstChessProgram,
X				&firstProgramPID, &toFirstProgFP, &fromFirstProgFP,
X				&firstProgramXID);
X		InitChessProgram(appData.secondHost, appData.secondChessProgram,
X			&secondProgramPID, &toSecondProgFP, &fromSecondProgFP,
X			&secondProgramXID);
X		lastGameMode = gameMode = TwoMachinesPlay;
X		SendToProgram("white\n", toSecondProgFP);
X		break;
X	}
X}
X
void
PauseProc()
X{
X	static int previous_mode = PauseGame;
X
X	switch (gameMode) {
X	case ForceMoves:
X	case EndOfGame:
X		return;
X	case PauseGame:
X		gameMode = previous_mode;
X		previous_mode = PauseGame;
X		DisplayClocks(ReStartTimers);
X		DisplayMessage("");
X		break;
X	case PlayFromGameFile:
X		if (readGameXID == NULL)
X			readGameXID = XtAddTimeOut((int) (1000 * appData.timeDelay),
X				ReadGameFile, NULL);
X		else {
X			XtRemoveTimeOut(readGameXID);
X			readGameXID = NULL;
X		}
X		DisplayMessage("Pausing");
X		break;
X	default:
X		if (currentMove == 0)	/* don't pause if no one has moved */
X			return;
X		previous_mode = gameMode;
X		gameMode = PauseGame;
X		DisplayClocks(StopTimers);
X		DisplayMessage("Pausing");
X		break;
X	}
X}
X
void
Iconify()
X{
X	Arg args[1];
X
X	fromX = fromY = -1;
X
X	XtSetArg(args[0], XtNiconic, True);
X	XtSetValues(shellWidget, args, 1);
X}
X
void
SendToProgram(message, fp)
X	char *message;
X	FILE *fp;
X{
X	if (appData.debugMode)
X		fprintf(stderr, "Sending to %s: %s\n",
X			fp == toFirstProgFP ? "first" : "second", message);
X	fputs(message, fp);
X}
X
void
ReceiveFromProgram(fp)
X	FILE *fp;
X{
X	char message[MSG_SIZ], *end_str;
X
X	if (fgets(message, MSG_SIZ, fp) == NULL)
X		return;
X
X	if ((end_str = (char *) strchr(message, '\r')) != NULL)
X		*end_str = '\0';
X	if ((end_str = (char *) strchr(message, '\n')) != NULL)
X		*end_str = '\0';
X
X	if (appData.debugMode)
X		fprintf(stderr, "Received from %s: %s\n",
X			fp == fromFirstProgFP ? "first" : "second", message);
X	HandleMachineMove(message);
X}
X
void
DisplayMessage(message)
X	char *message;
X{
X	Arg arg;
X
X	XtSetArg(arg, XtNlabel, message);
X	XtSetValues(messageWidget, &arg, 1);
X}
X
X/*
X * DisplayClocks manages the game clocks.
X *
X * In tournament play, black starts the clock and then white makes a move.
X * DisplayClocks starts black's clock when white makes the first move.
X * Also, DisplayClocks doesn't account for network lag so it could get
X * out of sync with GNU Chess's clock -- but then, referees are always right.
X */
void
DisplayClocks(clock_mode)
X	int clock_mode;
X{
X	switch (clock_mode) {
X	case ResetTimers:
X		if (timerXID != NULL) {
X			XtRemoveTimeOut(timerXID);
X			timerXID = NULL;
X		}
X
X		whiteTimeRemaining = appData.timeControl * 60;
X		DisplayTimerLabel(whiteTimerWidget, "White", whiteTimeRemaining);
X
X		blackTimeRemaining = appData.timeControl * 60;
X		DisplayTimerLabel(blackTimerWidget, "Black", blackTimeRemaining);
X		break;
X	case DisplayTimers:
X		if (gameMode == PauseGame)
X			return;
X		if (appData.clockMode) {
X			if (currentMove % 2 == 0) {
X				if ((currentMove > 0) || (gameMode == TwoMachinesPlay))
X					whiteTimeRemaining--;
X				if (whiteTimeRemaining <= 0) {
X					whiteTimeRemaining = 0;
X					DisplayMessage("White's flag dropped");
X				}
X			} else {
X				blackTimeRemaining--;
X				if (blackTimeRemaining <= 0) {
X					blackTimeRemaining = 0;
X					DisplayMessage("Black's flag dropped");
X				}
X			}
X		}
X
X		DisplayTimerLabel(whiteTimerWidget, "White", whiteTimeRemaining);
X		DisplayTimerLabel(blackTimerWidget, "Black", blackTimeRemaining);
X
X		/*
X		 * reset clocks when time control is acheived
X		 */
X		if (appData.clockMode
X			&& (((currentMove + 1) % (appData.movesPerSession * 2)) == 0)
X			&& (whiteTimeRemaining > 0) && (blackTimeRemaining > 0)) {
X			whiteTimeRemaining = appData.timeControl * 60;
X			blackTimeRemaining = appData.timeControl * 60;
X		}
X
X		if (timerXID != NULL) {
X			XtRemoveTimeOut(timerXID);
X			timerXID = NULL;
X		}
X		if (appData.clockMode)
X			timerXID = XtAddTimeOut(1000, DisplayClocks, DisplayTimers);
X		break;
X	case StopTimers:
X		if (timerXID == NULL)
X			return;
X		XtRemoveTimeOut(timerXID);
X		timerXID = NULL;
X		break;
X	case ReStartTimers:
X		if (timerXID != NULL)
X			return;
X
X		DisplayTimerLabel(whiteTimerWidget, "White", whiteTimeRemaining);
X		DisplayTimerLabel(blackTimerWidget, "Black", blackTimeRemaining);
X
X		if (appData.clockMode)
X			timerXID = XtAddTimeOut(1000, DisplayClocks, DisplayTimers);
X		break;
X	}
X}
X
void
DisplayTimerLabel(w, color, timer)
X	Widget w;
X	char *color;
X	time_t timer;
X{
X	char buf[MSG_SIZ];
X	Arg args[3];
X
X	if (appData.clockMode) {
X		sprintf(buf, "%s: %s", color, TimeString(timer));
X		XtSetArg(args[0], XtNlabel, buf);
X	} else
X		XtSetArg(args[0], XtNlabel, color);
X
X	if (((color[0] == 'B') && (currentMove % 2 == 0))
X		|| ((color[0] == 'W') && (currentMove % 2 == 1))) {
X		XtSetArg(args[1], XtNbackground, XBlackPixel(xDisplay, xScreen));
X		XtSetArg(args[2], XtNforeground, XWhitePixel(xDisplay, xScreen));
X	} else {
X		XtSetArg(args[1], XtNbackground, XWhitePixel(xDisplay, xScreen));
X		XtSetArg(args[2], XtNforeground, XBlackPixel(xDisplay, xScreen));
X	}
X	XtSetValues(w, args, 3);
X}
X
char *
TimeString(tm)
X	time_t tm;
X{
X	int second, minute, hour, day;
X	static char buf[32];
X
X	if (tm >= (60 * 60 * 24)) {
X		day = (int) (tm / (60 * 60 * 24));
X		tm -= day * 60 * 60 * 24;
X	} else
X		day = 0;
X
X	if (tm >= (60 * 60)) {
X		hour = (int) (tm / (60 * 60));
X		tm -= hour * 60 * 60;
X	} else
X		hour = 0;
X
X	if (tm >= 60) {
X		minute = (int) (tm / 60);
X		tm -= minute * 60;
X	} else
X		minute = 0;
X
X	second = tm % 60;
X
X	if (day > 0)
X		sprintf(buf, " %d:%02d:%02d:%02d ", day, hour, minute, second);
X	else if (hour > 0)
X		sprintf(buf, " %d:%02d:%02d ", hour, minute, second);
X	else
X		sprintf(buf, " %2d:%02d ", minute, second);
X
X	return buf;
X}
X
void
CatchPipeSignal()
X{
X	if (gameMode != BeginningOfGame) {
X		fprintf(stderr, "Caught pipe signal\n");
X		exit(0);
X	}
X}
X
void
Usage()
X{
X	fprintf(stderr, "Usage xboard:\n");
X	fprintf(stderr, "\tstandard Xt options\n");
X	fprintf(stderr, "\t-wpc or -whitePieceColor color\n");
X	fprintf(stderr, "\t-bpc or -blackPieceColor color\n");
X	fprintf(stderr, "\t-lsc or -lightSquareColor color\n");
X	fprintf(stderr, "\t-dsc or -darkSquareColor color\n");
X	fprintf(stderr, "\t-mps or -movesPerSession moves\n");
X	fprintf(stderr, "\t-init or -initString string\n");
X	fprintf(stderr, "\t-fcp or -firstChessProgram program_name\n");
X	fprintf(stderr, "\t-scp or -secondChessProgram program_name\n");
X	fprintf(stderr, "\t-fh or -firstHost host_name\n");
X	fprintf(stderr, "\t-sh or -secondHost host_name\n");
X	fprintf(stderr, "\t-spb or -solidPawnBitmap file_name\n");
X	fprintf(stderr, "\t-srb or -solidRookBitmap file_name\n");
X	fprintf(stderr, "\t-sbb or -solidBishopBitmap file_name\n");
X	fprintf(stderr, "\t-skb or -solidKnightBitmap file_name\n");
X	fprintf(stderr, "\t-sqb or -solidQueenBitmap file_name\n");
X	fprintf(stderr, "\t-skb or -solidKingBitmap file_name\n");
X	fprintf(stderr, "\t-opb or -outlinePawnBitmap file_name\n");
X	fprintf(stderr, "\t-orb or -outlineRookBitmap file_name\n");
X	fprintf(stderr, "\t-obb or -outlineBishopBitmap file_name\n");
X	fprintf(stderr, "\t-okb or -outlineKnightBitmap file_name\n");
X	fprintf(stderr, "\t-oqb or -outlineQueenBitmap file_name\n");
X	fprintf(stderr, "\t-okb or -outlineKingBitmap file_name\n");
X	fprintf(stderr, "\t-rsh or -remoteShell shell_name\n");
X	fprintf(stderr, "\t-td or -timeDelay seconds\n");
X	fprintf(stderr, "\t-tc or -timeControl minutes\n");
X	fprintf(stderr, "\t-sgf or -saveGameFile file_name\n");
X	fprintf(stderr, "\t-rgf or -readGameFile file_name\n");
X	fprintf(stderr, "\t-spf or -savePositionFile file_name\n");
X	fprintf(stderr, "\t-rpf or -readPositionFile file_name\n");
X	fprintf(stderr, "\t-mm or -matchMode (False | Init | Position | Opening)\n");
X	fprintf(stderr, "\t-mono or -monoMode (True | False)\n");
X	fprintf(stderr, "\t-debug or -debugMode (True | False)\n");
X	fprintf(stderr, "\t-clock or -clockMode (True | False)\n");
X	fprintf(stderr, "\t-big or -bigSizeMode (True | False)\n");
X	fprintf(stderr, "\t-st or -searchTime seconds\n");
X
X	exit(0);
X}
X
X/*
X * This is necessary because some C libraries aren't ANSI C compliant yet.
X */
char *
StrStr(string, match)
X	char *string, *match;
X{
X	int i, length;
X
X	length = strlen(match);
X
X	for (i = strlen(string) - length; i >= 0; i--, string++)
X		if (!strncmp(match, string, length))
X			return string;
X
X	return NULL;
X}
END_OF_FILE
if test 68496 -ne `wc -c <'./xboard.c'`; then
    echo shar: \"'./xboard.c'\" unpacked with wrong size!
fi
# end of './xboard.c'
fi
echo shar: End of shell archive.
exit 0


--
Dan Heller
------------------------------------------------
O'Reilly && Associates 		      Zyrcom Inc
Senior Writer			       President
argv@ora.com			argv@zipcode.com