2 * backend.c -- Common back end for X and Windows NT versions of
5 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
6 * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
8 * The following terms apply to Digital Equipment Corporation's copyright
10 * ------------------------------------------------------------------------
13 * Permission to use, copy, modify, and distribute this software and its
14 * documentation for any purpose and without fee is hereby granted,
15 * provided that the above copyright notice appear in all copies and that
16 * both that copyright notice and this permission notice appear in
17 * supporting documentation, and that the name of Digital not be
18 * used in advertising or publicity pertaining to distribution of the
19 * software without specific, written prior permission.
21 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
22 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
23 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
24 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
25 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28 * ------------------------------------------------------------------------
30 * The following terms apply to the enhanced version of XBoard distributed
31 * by the Free Software Foundation:
32 * ------------------------------------------------------------------------
33 * This program is free software; you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published by
35 * the Free Software Foundation; either version 2 of the License, or
36 * (at your option) any later version.
38 * This program is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41 * GNU General Public License for more details.
43 * You should have received a copy of the GNU General Public License
44 * along with this program; if not, write to the Free Software
45 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
46 * ------------------------------------------------------------------------
48 * See the file ChangeLog for a revision history. */
55 #include <sys/types.h>
64 #else /* not STDC_HEADERS */
67 # else /* not HAVE_STRING_H */
69 # endif /* not HAVE_STRING_H */
70 #endif /* not STDC_HEADERS */
73 # include <sys/fcntl.h>
74 #else /* not HAVE_SYS_FCNTL_H */
77 # endif /* HAVE_FCNTL_H */
78 #endif /* not HAVE_SYS_FCNTL_H */
80 #if TIME_WITH_SYS_TIME
81 # include <sys/time.h>
85 # include <sys/time.h>
91 #if defined(_amigados) && !defined(__GNUC__)
96 extern int gettimeofday(struct timeval *, struct timezone *);
104 #include "frontend.h"
111 #include "backendz.h"
112 #include "winboard.h"
114 /* A point in time */
116 long sec; /* Assuming this is >= 32 bits */
117 int ms; /* Assuming this is >= 16 bits */
120 int establish P((void));
121 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
122 char *buf, int count, int error));
123 void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
124 char *buf, int count, int error));
125 void SendToICS P((char *s));
126 void SendToICSDelayed P((char *s, long msdelay));
127 void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
129 void InitPosition P((int redraw));
130 void HandleMachineMove P((char *message, ChessProgramState *cps));
131 int AutoPlayOneMove P((void));
132 int LoadGameOneMove P((ChessMove readAhead));
133 int LoadGameFromFile P((char *filename, int n, char *title, int useList));
134 int LoadPositionFromFile P((char *filename, int n, char *title));
135 int SavePositionToFile P((char *filename));
136 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
138 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
139 void ShowMove P((int fromX, int fromY, int toX, int toY));
140 void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
141 /*char*/int promoChar));
142 void BackwardInner P((int target));
143 void ForwardInner P((int target));
144 void GameEnds P((ChessMove result, char *resultDetails, int whosays));
145 void EditPositionDone P((void));
146 void PrintOpponents P((FILE *fp));
147 void PrintPosition P((FILE *fp, int move));
148 void StartChessProgram P((ChessProgramState *cps));
149 void GuiCommand P((int command, int param));
150 void IcsAnalyzeOutPut P((ChessProgramState *cps, int endThink));
151 int SwitchGames P((void));
152 void SendToProgram P((char *message, ChessProgramState *cps));
153 void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
154 void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
155 char *buf, int count, int error));
156 void SendTimeControl P((ChessProgramState *cps,
157 int mps, long tc, int inc, int sd, int st));
158 char *TimeControlTagValue P((void));
159 void Attention P((ChessProgramState *cps));
160 void FeedMovesToProgram P((ChessProgramState *cps, int upto));
161 void ResurrectChessProgram P((void));
162 void DisplayComment P((int moveNumber, char *text));
163 void DisplayMove P((int moveNumber));
164 void GetTimeMark P((TimeMark *));
165 void ParseGameHistory P((char *game));
166 void ParseBoard12 P((char *string));
167 void StartClocks P((void));
168 void SwitchClocks P((void));
169 void StopClocks P((void));
170 void ResetClocks P((void));
171 char *PGNDate P((void));
172 void SetGameInfo P((void));
173 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
174 int RegisterMove P((void));
175 void MakeRegisteredMove P((void));
176 void TruncateGame P((void));
177 int looking_at P((char *, int *, char *));
178 void CopyPlayerNameIntoFileName P((char **, char *));
179 char *SavePart P((char *));
180 int SaveGameOldStyle P((FILE *));
181 int SaveGamePGN P((FILE *));
183 long SubtractTimeMarks P((TimeMark *, TimeMark *));
184 int CheckFlags P((void));
185 long NextTickLength P((long));
186 void CheckTimeControl P((void));
187 void show_bytes P((FILE *, char *, int));
188 int string_to_rating P((char *str));
189 void ParseFeatures P((char* args, ChessProgramState *cps));
190 void InitBackEnd3 P((void));
191 void FeatureDone P((ChessProgramState* cps, int val));
192 void InitChessProgram P((ChessProgramState *cps));
193 extern int tinyLayout, smallLayout;
195 void ParseZippyP3 P((char* data, char* player));
197 /* States for ics_getting_history */
199 #define H_REQUESTED 1
200 #define H_GOT_REQ_HEADER 2
201 #define H_GOT_UNREQ_HEADER 3
202 #define H_GETTING_MOVES 4
203 #define H_GOT_UNWANTED_HEADER 5
205 /* whosays values for GameEnds */
212 /* Maximum number of games in a cmail message */
213 #define CMAIL_MAX_GAMES 20
215 /* Different types of move when calling RegisterMove */
217 #define CMAIL_RESIGN 1
219 #define CMAIL_ACCEPT 3
221 /* Different types of result to remember for each game */
222 #define CMAIL_NOT_RESULT 0
223 #define CMAIL_OLD_RESULT 1
224 #define CMAIL_NEW_RESULT 2
226 /* Telnet protocol constants */
236 /* Fake up flags for now, as we aren't keeping track of castling
241 int flags = F_ALL_CASTLE_OK;
242 if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
243 switch (gameInfo.variant) {
245 case VariantGiveaway:
246 flags |= F_IGNORE_CHECK;
247 flags &= ~F_ALL_CASTLE_OK;
250 flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
252 case VariantKriegspiel:
253 flags |= F_KRIEGSPIEL_CAPTURE;
255 case VariantNoCastle:
256 flags &= ~F_ALL_CASTLE_OK;
264 FILE *gameFileFP, *debugFP;
266 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
267 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
268 char thinkOutput1[MSG_SIZ*10];
270 /*ChessProgramState first, second; */
272 /* premove variables */
275 int premoveFromX = 0;
276 int premoveFromY = 0;
277 int premovePromoChar = 0;
279 Boolean alarmSounded;
280 /* end premove variables */
282 #define ICS_GENERIC 0
285 #define ICS_CHESSNET 3 /* not really supported */
286 int ics_type = ICS_GENERIC;
287 char *ics_prefix = "$";
289 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
290 int pauseExamForwardMostMove = 0;
291 int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
292 int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
293 int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
294 int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
295 int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
296 int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
297 int whiteFlag = FALSE, blackFlag = FALSE;
298 int userOfferedDraw = FALSE;
299 int ics_user_moved = 0, ics_getting_history = H_FALSE, ics_gamenum = -1;
300 int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
301 int cmailMoveType[CMAIL_MAX_GAMES];
302 prefixHint = 0; /* PonderMove true/false */
303 long ics_clock_paused = 0;
304 ProcRef icsPR = NoProc, cmailPR = NoProc;
305 InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
306 gameMode = BeginningOfGame;
307 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
308 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
309 char white_holding[64], black_holding[64];
310 TimeMark lastNodeCountTime;
311 long lastNodeCount = 0;
312 int have_sent_ICS_logon = 0;
314 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
315 long timeRemaining[2][MAX_MOVES];
317 TimeMark programStartTime;
318 char ics_handle[MSG_SIZ];
319 int have_set_title = 0;
320 smartQueue icsQueue[max_gamenum];
328 /* Search stats from chessprogram */
330 char movelist[MSG_SIZ]; /* Last PV we were sent */
331 char ponderMove[12]; /* Current ponder move */
332 int depth; /* Current search depth */
333 int nr_moves; /* Total nr of root moves */
334 int moves_left; /* Moves remaining to be searched */
335 char move_name[MOVE_LEN]; /* Current move being searched, if provided */
336 unsigned long nodes; /* # of nodes searched */
337 int time; /* Search time (centiseconds) */
338 int score; /* Score (centipawns) */
339 int got_only_move; /* If last msg was "(only move)" */
340 int got_fail; /* 0 - nothing, 1 - got "--", 2 - got "++" */
341 int ok_to_send; /* handshaking between send & recv */
342 int line_is_book; /* 1 if movelist is book moves */
343 int seen_stat; /* 1 if we've seen the stat01: line */
344 int GUI_time; /* Time from EngineRoom */
347 static ChessProgramStats programStats;
349 /* animateTraining preserves the state of appData.animate
350 * when Training mode is activated. This allows the
351 * response to be animated when appData.animate == TRUE and
352 * appData.animateDragging == TRUE.
354 Boolean animateTraining;
360 Board boards[MAX_MOVES];
361 Board initialPosition = {
362 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
363 WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
364 { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
365 WhitePawn, WhitePawn, WhitePawn, WhitePawn },
366 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
367 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
368 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
369 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
370 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
371 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
372 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
373 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
374 { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
375 BlackPawn, BlackPawn, BlackPawn, BlackPawn },
376 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
377 BlackKing, BlackBishop, BlackKnight, BlackRook }
379 Board twoKingsPosition = {
380 { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
381 WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
382 { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
383 WhitePawn, WhitePawn, WhitePawn, WhitePawn },
384 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
385 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
386 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
387 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
388 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
389 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
390 { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
391 EmptySquare, EmptySquare, EmptySquare, EmptySquare },
392 { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
393 BlackPawn, BlackPawn, BlackPawn, BlackPawn },
394 { BlackRook, BlackKnight, BlackBishop, BlackQueen,
395 BlackKing, BlackKing, BlackKnight, BlackRook }
398 /* Convert str to a rating. Checks for special cases of "----",
399 "++++", etc. Also strips ()'s */
401 string_to_rating(str)
404 while(*str && !isdigit(*str)) ++str;
406 return 0; /* One of the special "no rating" cases */
414 /* Init programStats */
415 programStats.movelist[0] = NULLCHAR;
416 programStats.ponderMove[0] = 0;
417 programStats.depth = 0;
418 programStats.nr_moves = 0;
419 programStats.moves_left = 0;
420 programStats.nodes = 0;
421 programStats.time = 100;
422 programStats.score = 0;
423 programStats.got_only_move = 0;
424 programStats.got_fail = 0;
425 programStats.line_is_book = 0;
431 int matched, min, sec;
433 GetTimeMark(&programStartTime);
436 programStats.ok_to_send = 1;
437 programStats.seen_stat = 0;
441 * Initialize game list
447 * Internet chess server status
449 if (appData.icsActive) {
450 appData.matchMode = FALSE;
451 appData.matchGames = 0;
453 appData.noChessProgram = !appData.zippyPlay;
455 appData.zippyPlay = FALSE;
456 appData.zippyTalk = FALSE;
457 appData.noChessProgram = TRUE;
459 if (*appData.icsHelper != NULLCHAR) {
460 appData.useTelnet = TRUE;
461 appData.telnetProgram = appData.icsHelper;
464 appData.zippyTalk = appData.zippyPlay = FALSE;
468 * Parse timeControl resource
470 if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
471 appData.movesPerSession)) {
473 sprintf(buf, "bad timeControl option %s", appData.timeControl);
474 DisplayFatalError(buf, 0, 2);
478 * Parse searchTime resource
480 if (*appData.searchTime != NULLCHAR) {
481 matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
483 searchTime = min * 60;
484 } else if (matched == 2) {
485 searchTime = min * 60 + sec;
488 sprintf(buf, "bad searchTime option %s", appData.searchTime);
489 DisplayFatalError(buf, 0, 2);
493 first.which = "first";
494 second.which = "second";
495 first.maybeThinking = second.maybeThinking = FALSE;
496 first.pr = second.pr = NoProc;
497 first.isr = second.isr = NULL;
498 first.sendTime = second.sendTime = 2;
499 first.sendDrawOffers = 1;
500 if (appData.firstPlaysBlack) {
501 first.twoMachinesColor = "black\n";
502 second.twoMachinesColor = "white\n";
504 first.twoMachinesColor = "white\n";
505 second.twoMachinesColor = "black\n";
507 first.program = appData.firstChessProgram;
508 second.program = appData.secondChessProgram;
509 first.host = appData.firstHost;
510 second.host = appData.secondHost;
511 first.dir = appData.firstDirectory;
512 second.dir = appData.secondDirectory;
513 first.other = &second;
514 second.other = &first;
515 first.initString = appData.initString;
516 second.initString = appData.secondInitString;
517 first.computerString = appData.firstComputerString;
518 second.computerString = appData.secondComputerString;
519 first.useSigint = second.useSigint = TRUE;
520 first.useSigterm = second.useSigterm = TRUE;
521 first.reuse = appData.reuseFirst;
522 second.reuse = appData.reuseSecond;
523 first.useSetboard = second.useSetboard = FALSE;
524 first.useSAN = second.useSAN = FALSE;
525 first.usePing = second.usePing = FALSE;
526 first.lastPing = second.lastPing = 0;
527 first.lastPong = second.lastPong = 0;
528 first.usePlayother = second.usePlayother = FALSE;
529 first.useColors = second.useColors = TRUE;
530 first.useUsermove = second.useUsermove = FALSE;
531 first.sendICS = second.sendICS = FALSE;
532 first.sendName = second.sendName = appData.icsActive;
533 first.sdKludge = second.sdKludge = FALSE;
534 first.stKludge = second.stKludge = FALSE;
535 TidyProgramName(first.program, first.host, first.tidy);
536 TidyProgramName(second.program, second.host, second.tidy);
537 first.matchWins = second.matchWins = 0;
538 strcpy(first.variants, appData.variant);
539 strcpy(second.variants, appData.variant);
540 first.analysisSupport = second.analysisSupport = 2; /* detect */
541 first.analyzing = second.analyzing = FALSE;
542 first.initDone = second.initDone = FALSE;
544 if (appData.firstProtocolVersion > PROTOVER ||
545 appData.firstProtocolVersion < 1) {
547 sprintf(buf, "protocol version %d not supported",
548 appData.firstProtocolVersion);
549 DisplayFatalError(buf, 0, 2);
551 first.protocolVersion = appData.firstProtocolVersion;
554 if (appData.secondProtocolVersion > PROTOVER ||
555 appData.secondProtocolVersion < 1) {
557 sprintf(buf, "protocol version %d not supported",
558 appData.secondProtocolVersion);
559 DisplayFatalError(buf, 0, 2);
561 second.protocolVersion = appData.secondProtocolVersion;
564 if (appData.icsActive) {
565 appData.clockMode = TRUE; /* changes dynamically in ICS mode */
566 } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
567 appData.clockMode = FALSE;
568 first.sendTime = second.sendTime = 0;
572 /* Override some settings from environment variables, for backward
573 compatibility. Unfortunately it's not feasible to have the env
574 vars just set defaults, at least in xboard. Ugh.
576 if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
581 if (appData.noChessProgram) {
582 programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
583 + strlen(PATCHLEVEL));
584 sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
588 while (*q != ' ' && *q != NULLCHAR) q++;
590 while (p > first.program && *(p-1) != '/') p--;
591 programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
592 + strlen(PATCHLEVEL) + (q - p));
593 sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
594 strncat(programVersion, p, q - p);
597 if (!appData.icsActive) {
599 /* Check for variants that are supported only in ICS mode,
600 or not at all. Some that are accepted here nevertheless
601 have bugs; see comments below.
603 VariantClass variant = StringToVariant(appData.variant);
605 case VariantBughouse: /* need four players and two boards */
606 case VariantKriegspiel: /* need to hide pieces and move details */
607 case VariantFischeRandom: /* castling doesn't work, shuffle not done */
608 sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);
609 DisplayFatalError(buf, 0, 2);
613 case VariantLoadable:
623 sprintf(buf, "Unknown variant name %s", appData.variant);
624 DisplayFatalError(buf, 0, 2);
627 case VariantNormal: /* definitely works! */
628 case VariantWildCastle: /* pieces not automatically shuffled */
629 case VariantNoCastle: /* pieces not automatically shuffled */
630 case VariantCrazyhouse: /* holdings not shown,
631 offboard interposition not understood */
632 case VariantLosers: /* should work except for win condition,
633 and doesn't know captures are mandatory */
634 case VariantSuicide: /* should work except for win condition,
635 and doesn't know captures are mandatory */
636 case VariantGiveaway: /* should work except for win condition,
637 and doesn't know captures are mandatory */
638 case VariantTwoKings: /* should work */
639 case VariantAtomic: /* should work except for win condition */
640 case Variant3Check: /* should work except for win condition */
641 case VariantShatranj: /* might work if TestLegality is off */
648 ParseTimeControl(tc, ti, mps)
653 int matched, min, sec;
655 matched = sscanf(tc, "%d:%d", &min, &sec);
657 timeControl = min * 60 * 1000;
658 } else if (matched == 2) {
659 timeControl = (min * 60 + sec) * 1000;
665 timeIncrement = ti * 1000; /* convert to ms */
669 movesPerSession = mps;
677 if (appData.debugMode) {
678 fprintf(debugFP, "%s\n", programVersion);
681 if (appData.matchGames > 0) {
682 appData.matchMode = TRUE;
683 } else if (appData.matchMode) {
684 appData.matchGames = 1;
687 if (appData.noChessProgram || first.protocolVersion == 1) {
690 /* kludge: allow timeout for initial "feature" commands */
692 if (appData.icsActive) {
693 DisplayMessage("", "ICS-Mode: Waiting for chessprogram...");
695 DisplayMessage("", "Starting chessprogram");
697 ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
702 InitBackEnd3 P((void))
704 GameMode initialMode;
708 InitChessProgram(&first);
710 /* Make a console window if needed */
711 if (appData.icsActive) {
717 if (appData.icsActive) {
720 if (*appData.icsCommPort != NULLCHAR) {
721 sprintf(buf, "Could not open comm port %s",
722 appData.icsCommPort);
724 sprintf(buf, "Could not connect to host %s, port %s",
725 appData.icsHost, appData.icsPort);
727 DisplayFatalError(buf, err, 1);
732 AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
734 AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
735 } else if (appData.noChessProgram) {
741 if (*appData.cmailGameName != NULLCHAR) {
743 OpenLoopback(&cmailPR);
745 AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
749 DisplayMessage("", "");
750 if (StrCaseCmp(appData.initialMode, "") == 0) {
751 initialMode = BeginningOfGame;
752 } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
753 initialMode = TwoMachinesPlay;
754 } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
755 initialMode = AnalyzeFile;
756 } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
757 initialMode = AnalyzeMode;
758 } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
759 initialMode = MachinePlaysWhite;
760 } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
761 initialMode = MachinePlaysBlack;
762 } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
763 initialMode = EditGame;
764 } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
765 initialMode = EditPosition;
766 } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
767 initialMode = Training;
769 sprintf(buf, "Unknown initialMode %s", appData.initialMode);
770 DisplayFatalError(buf, 0, 2);
774 if (appData.matchMode) {
775 /* Set up machine vs. machine match */
776 if (appData.noChessProgram) {
777 DisplayFatalError("Can't have a match with no chess programs",
783 if (*appData.loadGameFile != NULLCHAR) {
784 if (!LoadGameFromFile(appData.loadGameFile,
785 appData.loadGameIndex,
786 appData.loadGameFile, FALSE)) {
787 DisplayFatalError("Bad game file", 0, 1);
790 } else if (*appData.loadPositionFile != NULLCHAR) {
791 if (!LoadPositionFromFile(appData.loadPositionFile,
792 appData.loadPositionIndex,
793 appData.loadPositionFile)) {
794 DisplayFatalError("Bad position file", 0, 1);
799 } else if (*appData.cmailGameName != NULLCHAR) {
800 /* Set up cmail mode */
801 ReloadCmailMsgEvent(TRUE);
803 /* Set up other modes */
804 if (initialMode == AnalyzeFile) {
805 if (*appData.loadGameFile == NULLCHAR) {
806 DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);
810 if (*appData.loadGameFile != NULLCHAR) {
811 (void) LoadGameFromFile(appData.loadGameFile,
812 appData.loadGameIndex,
813 appData.loadGameFile, TRUE);
814 } else if (*appData.loadPositionFile != NULLCHAR) {
815 (void) LoadPositionFromFile(appData.loadPositionFile,
816 appData.loadPositionIndex,
817 appData.loadPositionFile);
819 if (initialMode == AnalyzeMode) {
820 if (appData.noChessProgram) {
821 DisplayFatalError("Analysis mode requires a chess engine", 0, 2);
824 if (appData.icsActive) {
825 DisplayFatalError("Analysis mode does not work with ICS mode",0,2);
829 } else if (initialMode == AnalyzeFile) {
830 ShowThinkingEvent(TRUE);
832 AnalysisPeriodicEvent(1);
833 } else if (initialMode == MachinePlaysWhite) {
834 if (appData.noChessProgram) {
835 DisplayFatalError("MachineWhite mode requires a chess engine",
839 if (appData.icsActive) {
840 DisplayFatalError("MachineWhite mode does not work with ICS mode",
845 } else if (initialMode == MachinePlaysBlack) {
846 if (appData.noChessProgram) {
847 DisplayFatalError("MachineBlack mode requires a chess engine",
851 if (appData.icsActive) {
852 DisplayFatalError("MachineBlack mode does not work with ICS mode",
857 } else if (initialMode == TwoMachinesPlay) {
858 if (appData.noChessProgram) {
859 DisplayFatalError("TwoMachines mode requires a chess engine",
863 if (appData.icsActive) {
864 DisplayFatalError("TwoMachines mode does not work with ICS mode",
869 } else if (initialMode == EditGame) {
871 } else if (initialMode == EditPosition) {
873 } else if (initialMode == Training) {
874 if (*appData.loadGameFile == NULLCHAR) {
875 DisplayFatalError("Training mode requires a game file", 0, 2);
881 /* If EngineRoom TRUE start */
882 if (appData.AnalysisWindow && appData.showThinking &&
883 !appData.noChessProgram) {
884 if (appData.icsActive && appData.zippyPlay) {
885 AnalysisPopUp(0,0,0,0,0,0,0,5);
887 if (!appData.icsActive) AnalysisPopUp(0,0,0,0,0,0,0,5);
888 StartAnalysisClock();
893 * Establish will establish a contact to a remote host.port.
894 * Sets icsPR to a ProcRef for a process (or pseudo-process)
895 * used to talk to the host.
896 * Returns 0 if okay, error code if not.
903 if (*appData.icsCommPort != NULLCHAR) {
904 /* Talk to the host through a serial comm port */
905 return OpenCommPort(appData.icsCommPort, &icsPR);
907 } else if (*appData.gateway != NULLCHAR) {
908 if (*appData.remoteShell == NULLCHAR) {
909 /* Use the rcmd protocol to run telnet program on a gateway host */
910 sprintf(buf, "%s %s %s",
911 appData.telnetProgram, appData.icsHost, appData.icsPort);
912 return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
915 /* Use the rsh program to run telnet program on a gateway host */
916 if (*appData.remoteUser == NULLCHAR) {
917 sprintf(buf, "%s %s %s %s %s", appData.remoteShell,
918 appData.gateway, appData.telnetProgram,
919 appData.icsHost, appData.icsPort);
921 sprintf(buf, "%s %s -l %s %s %s %s",
922 appData.remoteShell, appData.gateway,
923 appData.remoteUser, appData.telnetProgram,
924 appData.icsHost, appData.icsPort);
926 return StartChildProcess(buf, "", &icsPR);
929 } else if (appData.useTelnet) {
930 return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
933 /* TCP socket interface differs somewhat between
934 Unix and NT; handle details in the front end.
936 return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
941 show_bytes(fp, buf, count)
947 if (*buf < 040 || *(unsigned char *) buf > 0177) {
948 fprintf(fp, "\\%03o", *buf & 0xff);
957 /* Returns an errno value */
959 OutputMaybeTelnet(pr, message, count, outError)
965 char buf[8192], *p, *q, *buflim;
966 int left, newcount, outcount;
968 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
969 *appData.gateway != NULLCHAR) {
970 if (appData.debugMode) {
971 fprintf(debugFP, ">ICS: ");
972 show_bytes(debugFP, message, count);
973 fprintf(debugFP, "\n");
975 return OutputToProcess(pr, message, count, outError);
978 buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
985 if (appData.debugMode) {
986 fprintf(debugFP, ">ICS: ");
987 show_bytes(debugFP, buf, newcount);
988 fprintf(debugFP, "\n");
990 outcount = OutputToProcess(pr, buf, newcount, outError);
991 if (outcount < newcount) return -1; /* to be sure */
998 } else if (((unsigned char) *p) == TN_IAC) {
999 *q++ = (char) TN_IAC;
1006 if (appData.debugMode) {
1007 fprintf(debugFP, ">ICS: ");
1008 show_bytes(debugFP, buf, newcount);
1009 fprintf(debugFP, "\n");
1011 outcount = OutputToProcess(pr, buf, newcount, outError);
1012 if (outcount < newcount) return -1; /* to be sure */
1017 read_from_player(isr, closure, message, count, error)
1024 int outError, outCount;
1025 static int gotEof = 0;
1027 /* Pass data read from player on to ICS */
1030 outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
1031 if (outCount < count) {
1032 DisplayFatalError("Error writing to ICS", outError, 1);
1034 } else if (count < 0) {
1035 RemoveInputSource(isr);
1036 DisplayFatalError("Error reading from keyboard", error, 1);
1037 } else if (gotEof++ > 0) {
1038 RemoveInputSource(isr);
1039 DisplayFatalError("Got end of file from keyboard", 0, 0);
1047 int count, outCount, outError;
1049 if (icsPR == NULL) return;
1052 outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
1053 if (outCount < count) {
1054 DisplayFatalError("Error writing to ICS", outError, 1);
1058 /* This is used for sending logon scripts to the ICS. Sending
1059 without a delay causes problems when using timestamp on ICC
1060 (at least on my machine). */
1062 SendToICSDelayed(s,msdelay)
1066 int count, outCount, outError;
1068 if (icsPR == NULL) return;
1071 if (appData.debugMode) {
1072 fprintf(debugFP, ">ICS: ");
1073 show_bytes(debugFP, s, count);
1074 fprintf(debugFP, "\n");
1076 outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
1078 if (outCount < count) {
1079 DisplayFatalError("Error writing to ICS", outError, 1);
1084 /* Remove all highlighting escape sequences in s
1085 Also deletes any suffix starting with '('
1088 StripHighlightAndTitle(s)
1091 static char retbuf[MSG_SIZ];
1094 while (*s != NULLCHAR) {
1095 while (*s == '\033') {
1096 while (*s != NULLCHAR && !isalpha(*s)) s++;
1097 if (*s != NULLCHAR) s++;
1099 while (*s != NULLCHAR && *s != '\033') {
1100 if (*s == '(' || *s == '[') {
1111 /* Remove all highlighting escape sequences in s */
1116 static char retbuf[MSG_SIZ];
1119 while (*s != NULLCHAR) {
1120 while (*s == '\033') {
1121 while (*s != NULLCHAR && !isalpha(*s)) s++;
1122 if (*s != NULLCHAR) s++;
1124 while (*s != NULLCHAR && *s != '\033') {
1132 char *variantNames[] = VARIANT_NAMES;
1137 return variantNames[v];
1141 /* Identify a variant from the strings the chess servers use or the
1142 PGN Variant tag names we use. */
1149 VariantClass v = VariantNormal;
1150 int i, found = FALSE;
1155 for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
1156 if (StrCaseStr(e, variantNames[i])) {
1157 v = (VariantClass) i;
1164 if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
1165 || StrCaseStr(e, "wild/fr")) {
1166 v = VariantFischeRandom;
1167 } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
1168 (i = 1, p = StrCaseStr(e, "w"))) {
1170 while (*p && (isspace(*p) || *p == '(')) p++;
1177 case 0: /* FICS only, actually */
1179 /* Castling legal even if K starts on d-file */
1180 v = VariantWildCastle;
1185 /* Castling illegal even if K & R happen to start in
1186 normal positions. */
1187 v = VariantNoCastle;
1200 /* Castling legal iff K & R start in normal positions */
1206 /* Special wilds for position setup; unclear what to do here */
1207 v = VariantLoadable;
1210 /* Bizarre ICC game */
1211 v = VariantTwoKings;
1214 v = VariantKriegspiel;
1220 v = VariantFischeRandom;
1223 v = VariantCrazyhouse;
1226 v = VariantBughouse;
1232 /* Not quite the same as FICS suicide! */
1233 v = VariantGiveaway;
1239 v = VariantShatranj;
1242 /* Temporary names for future ICC types. The name *will* change in
1243 the next xboard/WinBoard release after ICC defines it. */
1270 /* Found "wild" or "w" in the string but no number;
1271 must assume it's normal chess. */
1275 sprintf(buf, "Unknown wild type %d", wnum);
1276 DisplayError(buf, 0);
1282 if (appData.debugMode) {
1283 fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",
1284 e, wnum, VariantName(v));
1289 static int leftover_start = 0, leftover_len = 0;
1290 char star_match[STAR_MATCH_N][MSG_SIZ];
1292 /* Test whether pattern is present at &buf[*index]; if so, return TRUE,
1293 advance *index beyond it, and set leftover_start to the new value of
1294 *index; else return FALSE. If pattern contains the character '*', it
1295 matches any sequence of characters not containing '\r', '\n', or the
1296 character following the '*' (if any), and the matched sequence(s) are
1297 copied into star_match.
1300 looking_at(buf, index, pattern)
1305 char *bufp = &buf[*index], *patternp = pattern;
1307 char *matchp = star_match[0];
1310 if (*patternp == NULLCHAR) {
1311 *index = leftover_start = bufp - buf;
1315 if (*bufp == NULLCHAR) return FALSE;
1316 if (*patternp == '*') {
1317 if (*bufp == *(patternp + 1)) {
1319 matchp = star_match[++star_count];
1323 } else if (*bufp == '\n' || *bufp == '\r') {
1325 if (*patternp == NULLCHAR)
1330 *matchp++ = *bufp++;
1334 if (*patternp != *bufp) return FALSE;
1341 SendToPlayer(data, length)
1345 int error, outCount;
1346 outCount = OutputToProcess(NoProc, data, length, &error);
1347 if (outCount < length) {
1348 DisplayFatalError("Error writing to display", error, 1);
1353 PackHolding(packed, holding)
1365 switch (runlength) {
1376 sprintf(q, "%d", runlength);
1388 /* Telnet protocol requests from the front end */
1390 TelnetRequest(ddww, option)
1391 unsigned char ddww, option;
1393 unsigned char msg[3];
1394 int outCount, outError;
1396 if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
1398 if (appData.debugMode) {
1399 char buf1[8], buf2[8], *ddwwStr, *optionStr;
1415 sprintf(buf1, "%d", ddww);
1424 sprintf(buf2, "%d", option);
1427 fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
1432 outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
1434 DisplayFatalError("Error writing to ICS", outError, 1);
1441 if (!appData.icsActive) return;
1442 TelnetRequest(TN_DO, TN_ECHO);
1448 if (!appData.icsActive) return;
1449 TelnetRequest(TN_DONT, TN_ECHO);
1452 static int loggedOn = FALSE;
1454 /*-- Game start info cache: --*/
1456 char gs_kind[MSG_SIZ];
1457 static char player1Name[128] = "";
1458 static char player2Name[128] = "";
1459 static int player1Rating = -1;
1460 static int player2Rating = -1;
1461 /*----------------------------*/
1464 /* ICC user send showinfo <game number> und we send latest pv */
1465 /* If username beginn with guest* send advertisement ;-) */
1467 showInfo(data, player)
1476 /* this feature is only for ICC admins or chessprogramer */
1477 /* You must set commandline /icc to use this */
1478 if (appData.userVersion == TRUE) return;
1479 if (appData.ICC_feature == FALSE || ics_type != ICS_ICC) return;
1480 while (*ptr == ' ') ptr++;
1481 if (*ptr == 0 || (sizeof(ptr) > MSG_SIZ)) return;
1482 sscanf(ptr, "%s", buf);
1484 /* showgames command */
1485 if (strncmp(buf, "showgames", 9) == 0) {
1488 for (counter = 1; counter <= max_gamenum; counter++) {
1489 if (icsQueue[counter].killPv > 0) {
1490 sprintf(temp, "tell %s I analyze game %d (%s/%s)\n", player, counter,
1491 icsQueue[counter].white, icsQueue[counter].black);
1498 i = atoi(buf); /* security first - user could try to buffer overflow data ! */
1499 if (i > max_gamenum || icsQueue[i].killPv == 0) {
1500 sprintf(temp, "tell %s I don't analysis this game\n", player);
1502 sprintf(temp, "tell %s \"tell %s showgames\" to see which games are currently analyzed.\n", player, ics_handle);
1504 } else if (icsQueue[i].killPv > 0) {
1505 sprintf(temp, "tell %s Hello %s from Winboard-DM\n", player, player);
1507 /* pos after "Depth " */
1508 if (icsQueue[i].lastpv[0]) {
1509 sprintf(temp, "tell %s Last analyze of game %d (%s/%s): (%s) Depth=%s\n",
1510 player, i, icsQueue[i].white, icsQueue[i].black, first.tidy, icsQueue[i].lastpv);
1512 sprintf(temp, "tell %s Sorry, no analysis avaible from game %d at the moment. Please try it later again.\n",
1516 /* for ICC guest send member and register info */
1517 if (strncmp(player, "guest", 5) == 0) {
1518 sprintf(temp, "tell %s Be member of ICC. Get a free 7-days trial membership: \"http://www.chessclub.com/register/english.html\" For more information type: \"help reg\"\n", player);
1525 Call this to see if we have a game where we not send a message
1526 for the current move
1535 for (counter = 1; counter <= max_gamenum; counter++) {
1536 if (icsQueue[counter].killPv > 0 && icsQueue[counter].CurrentMove >=
1537 icsQueue[counter].MessageMove && icsQueue[counter].flag == 0) {
1538 if (appData.debugMode) fprintf(debugFP, "ICS-Analyze: We switch to game %d \n", counter);
1539 sprintf(buf, "refresh %d \n", counter);
1548 /* parse and action from zippy password3 */
1550 ParseZippyP3(data, player)
1560 if (appData.userVersion == TRUE) return;
1564 sprintf(temp, "tell %s Sorry, to many values. Max 6 values each line. Try again.\n", player);
1566 sprintf(temp, "tell %s Or try send me \"help\"\n", player);
1570 while (*ptr == ' ') ptr++;
1571 if (*ptr == 0) break;
1572 sscanf(ptr, "%s", command[counter].string);
1573 if (appData.debugMode) fprintf(debugFP, "zp3: %s\n", command[counter].string);
1574 ptr = ptr + strlen(command[counter].string);
1577 if(strncmp(command[forward].string, "help", 4) == 0) {
1578 /* icc don't accept text after new line :((( */
1579 /* So, we must write every line */
1580 sprintf(temp, "tell %s Hello %s from %s %s%s\n", player, player, PRODUCT, VERSION, PATCHLEVEL);
1582 sprintf(temp, "tell %s analyze <observe|follow|unobserve|unfollow> <Playername|Gamenumber>\n", player);
1584 sprintf(temp, "tell %s analyze <start|stop>\n", player);
1586 sprintf(temp, "tell %s analyze output <whisper|kibitz|tell|none> if tell <value>\n", player);
1588 sprintf(temp, "tell %s analyze killpv <value> <Gamenumber>\n", player);
1590 sprintf(temp, "tell %s if you are setup killpv you enable it automaticly\n", player);
1592 sprintf(temp, "tell %s analyze smartqueue <1|0>\n", player);
1594 sprintf(temp, "tell %s analyze show <Gamenumber>\n", player);
1596 sprintf(temp, "tell %s engine reset\n", player);
1598 sprintf(temp, "tell %s <whisper|kibitz>\n", player);
1603 if (strncmp(command[forward].string, "engine", 6) == 0) {
1604 if (strncmp(command[forward+1].string, "reset", 5) == 0) {
1605 sprintf(temp, "tell %s reset engine...\n", player);
1607 ResurrectChessProgram();
1612 if (strncmp(command[forward].string, "whisper", 7) == 0) {
1613 sprintf(temp, "tell %s GUI = whisper\n", player);
1615 appData.SendOutPutToICS = 1;
1617 } else if (trncmp(command[forward].string, "kibitz", 6) == 0) {
1618 sprintf(temp, "tell %s GUI = kibitz\n", player);
1620 appData.SendOutPutToICS = 2;
1624 if(gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) {
1625 sprintf(temp, "tell %s Sorry, i'm playing a game!\n", player);
1629 if (strncmp(command[forward].string, "analyze", 10) == 0) {
1630 if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
1631 strncmp(command[forward+1].string, "follow", 6) == 0 ||
1632 strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
1633 strncmp(command[forward+1].string, "unfollow", 8) == 0) {
1635 /* reset Queue if leave */
1636 if (strncmp(command[forward+1].string, "unobserve", 9) == 0 ||
1637 strncmp(command[forward+1].string, "unfollow", 8) == 0) ResetIcsQueue(ics_gamenum);
1640 if(gameMode != IcsPlayingWhite || gameMode != IcsPlayingBlack) {
1641 sprintf(temp, "%s %s\n", command[forward+1].string,
1642 command[forward+2].string);
1644 sprintf(temp, "tell %s action: %s %s\n", player, command[forward+1].string,
1645 command[forward+2].string);
1646 if (strncmp(command[forward+1].string, "observe", 7) == 0 ||
1647 strncmp(command[forward+1].string, "follow", 6) == 0) {
1649 if (ics_gamenum > max_gamenum) {
1650 sprintf(temp, "tell %s gamenumber to high\n", player);
1656 sprintf(temp, "tell %s Sorry, i'm playing\n", player);
1662 if (strncmp(command[forward+1].string, "start", 5) == 0) {
1663 if (gameMode != IcsObserving) {
1664 sprintf(temp, "tell %s I must observe a game\n", player);
1665 } else if (gameMode == IcsObserving) {
1666 sprintf(temp, "tell %s Starting ICS analyze...\n", player);
1667 appData.icsAnalyze = TRUE;
1673 if (strncmp(command[forward+1].string, "stop", 4) == 0) {
1674 if (gameMode != IcsObserving) {
1675 sprintf(temp, "tell %s I must observe a game\n", player);
1676 } else if (gameMode == IcsObserving) {
1677 sprintf(temp, "tell %s Stopping ICS analyze...\n", player);
1683 if (strncmp(command[forward+1].string, "output", 6) == 0) {
1684 if(strncmp(command[forward+2].string, "whisper", 7) == 0) {
1685 appData.icsAnalyzeOutPut = 1;
1686 sprintf(temp, "tell %s analyze output is whisper\n", player);
1687 } else if (strncmp(command[forward+2].string, "kibitz", 6) == 0) {
1688 appData.icsAnalyzeOutPut = 2;
1689 sprintf(temp, "tell %s analyze output is kibitz\n", player);
1690 } else if (strncmp(command[forward+2].string, "tell", 4) == 0) {
1691 appData.icsAnalyzeOutPut = 3;
1692 if (command[forward+3].string[0] != NULLCHAR) {
1693 if (strncmp(command[forward+3].string, "none", 4) == 0) {
1694 appData.icsAnalyzeOutPut = 4;
1695 sprintf(temp, "tell %s analyze output is: none\n", player);
1699 strcpy(appData.icsTells, command[forward+3].string);
1700 sprintf(temp, "tell %s analyze output is tell: %s\n", player,
1703 sprintf(temp, "tell %s Error: I don't know where i should send my analysis\n", player);
1704 appData.icsAnalyzeOutPut = 4;
1709 if (strncmp(command[forward+1].string, "killpv", 6) == 0) {
1710 i = atoi(command[forward+3].string);
1711 j = atoi(command[forward+2].string);
1712 if (i <= max_gamenum && i != 0) {
1713 if (icsQueue[i].killPv == 0) {
1714 sprintf(temp, "tell %s Error: Mh, killpv is zero. Wrong gamenumber?\n", player);
1717 if ( j > 20 || j < 1) {
1718 sprintf(temp, "tell %s Error: killpv must be 1-20\n", player);
1720 sprintf(temp, "tell %s killpv for game %d is now %d\n", player, i, j);
1721 icsQueue[i].killPv = j;
1722 appData.icsEngineKillPV = TRUE;
1727 sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
1732 if (strncmp(command[forward+1].string, "smartqueue", 10) == 0) {
1733 j = atoi(command[forward+2].string);
1734 if (j == 0 || j == 1) {
1735 appData.smartQueue = j;
1736 sprintf(temp, "tell %s smartqueue is now: %d", player, appData.smartQueue);
1738 sprintf(temp, "tell %s Error: not possible smartquere value\n", player);
1743 if (strncmp(command[forward+1].string, "show", 4) == 0) {
1744 i = atoi(command[forward+2].string);
1745 if (i <= max_gamenum && i != 0) {
1746 if (icsQueue[i].killPv == 0) {
1747 sprintf(temp, "tell %s Error: emtpy game slot. Wrong gamenumber?\n", player);
1750 sprintf(temp, "tell %s summary information about game: %d\n", player, i);
1752 sprintf(temp, "tell %s enable killpv: %d, killpv value = %d\n", player,
1753 appData.icsEngineKillPV, icsQueue[i].killPv);
1755 sprintf(temp, "tell %s enable smartqueue (all games): %d\n", player, appData.smartQueue);
1757 sprintf(temp, "tell %s analyze output (all games) is %d\n", player,
1758 appData.icsAnalyzeOutPut);
1762 sprintf(temp, "tell %s Error: not possible gamenumber\n", player);
1771 read_from_ics(isr, closure, data, count, error)
1778 #define BUF_SIZE 8192
1779 #define STARTED_NONE 0
1780 #define STARTED_MOVES 1
1781 #define STARTED_BOARD 2
1782 #define STARTED_OBSERVE 3
1783 #define STARTED_HOLDINGS 4
1784 #define STARTED_CHATTER 5
1785 #define STARTED_COMMENT 6
1786 #define STARTED_MOVES_NOHIDE 7
1788 static int started = STARTED_NONE;
1789 static char parse[20000];
1790 static int parse_pos = 0;
1791 static char buf[BUF_SIZE + 1];
1792 static int firstTime = TRUE, intfSet = FALSE;
1793 static ColorClass curColor = ColorNormal;
1794 static ColorClass prevColor = ColorNormal;
1795 static int savingComment = FALSE;
1801 /* extra zippy vars */
1806 char reply[MSG_SIZ];
1809 /* If last read ended with a partial line that we couldn't parse,
1810 prepend it to the new read and try again. */
1811 if (leftover_len > 0) {
1812 for (i=0; i<leftover_len; i++)
1813 buf[i] = buf[leftover_start + i];
1816 /* Copy in new characters, removing nulls and \r's */
1817 buf_len = leftover_len;
1818 for (i = 0; i < count; i++) {
1819 if (data[i] != NULLCHAR && data[i] != '\r')
1820 buf[buf_len++] = data[i];
1823 buf[buf_len] = NULLCHAR;
1824 next_out = leftover_len;
1828 while (i < buf_len) {
1829 /* Deal with part of the TELNET option negotiation
1830 protocol. We refuse to do anything beyond the
1831 defaults, except that we allow the WILL ECHO option,
1832 which ICS uses to turn off password echoing when we are
1833 directly connected to it. We reject this option
1834 if localLineEditing mode is on (always on in xboard)
1835 and we are talking to port 23, which might be a real
1836 telnet server that will try to keep WILL ECHO on permanently.
1838 if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
1839 static int remoteEchoOption = FALSE; /* telnet ECHO option */
1840 unsigned char option;
1842 switch ((unsigned char) buf[++i]) {
1844 if (appData.debugMode)
1845 fprintf(debugFP, "\n<WILL ");
1846 switch (option = (unsigned char) buf[++i]) {
1848 if (appData.debugMode)
1849 fprintf(debugFP, "ECHO ");
1850 /* Reply only if this is a change, according
1851 to the protocol rules. */
1852 if (remoteEchoOption) break;
1853 if (appData.localLineEditing &&
1854 atoi(appData.icsPort) == TN_PORT) {
1855 TelnetRequest(TN_DONT, TN_ECHO);
1858 TelnetRequest(TN_DO, TN_ECHO);
1859 remoteEchoOption = TRUE;
1863 if (appData.debugMode)
1864 fprintf(debugFP, "%d ", option);
1865 /* Whatever this is, we don't want it. */
1866 TelnetRequest(TN_DONT, option);
1871 if (appData.debugMode)
1872 fprintf(debugFP, "\n<WONT ");
1873 switch (option = (unsigned char) buf[++i]) {
1875 if (appData.debugMode)
1876 fprintf(debugFP, "ECHO ");
1877 /* Reply only if this is a change, according
1878 to the protocol rules. */
1879 if (!remoteEchoOption) break;
1881 TelnetRequest(TN_DONT, TN_ECHO);
1882 remoteEchoOption = FALSE;
1885 if (appData.debugMode)
1886 fprintf(debugFP, "%d ", (unsigned char) option);
1887 /* Whatever this is, it must already be turned
1888 off, because we never agree to turn on
1889 anything non-default, so according to the
1890 protocol rules, we don't reply. */
1895 if (appData.debugMode)
1896 fprintf(debugFP, "\n<DO ");
1897 switch (option = (unsigned char) buf[++i]) {
1899 /* Whatever this is, we refuse to do it. */
1900 if (appData.debugMode)
1901 fprintf(debugFP, "%d ", option);
1902 TelnetRequest(TN_WONT, option);
1907 if (appData.debugMode)
1908 fprintf(debugFP, "\n<DONT ");
1909 switch (option = (unsigned char) buf[++i]) {
1911 if (appData.debugMode)
1912 fprintf(debugFP, "%d ", option);
1913 /* Whatever this is, we are already not doing
1914 it, because we never agree to do anything
1915 non-default, so according to the protocol
1916 rules, we don't reply. */
1921 if (appData.debugMode)
1922 fprintf(debugFP, "\n<IAC ");
1923 /* Doubled IAC; pass it through */
1927 if (appData.debugMode)
1928 fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
1929 /* Drop all other telnet commands on the floor */
1932 if (oldi > next_out)
1933 SendToPlayer(&buf[next_out], oldi - next_out);
1939 /* OK, this at least will *usually* work */
1940 if (!loggedOn && looking_at(buf, &i, "ics%")) {
1944 if (loggedOn && !intfSet) {
1945 if (ics_type == ICS_ICC) {
1947 "/set-quietly interface %s\n/set-quietly style 12\n",
1950 } else if (ics_type == ICS_CHESSNET) {
1951 sprintf(str, "/style 12\n");
1953 strcpy(str, "alias $ @\n$set interface ");
1954 strcat(str, programVersion);
1955 strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
1957 strcat(str, "$iset nohighlight 1\n");
1959 strcat(str, "$iset lock 1\n$style 12\n");
1965 if (started == STARTED_COMMENT) {
1966 /* Accumulate characters in comment */
1967 parse[parse_pos++] = buf[i];
1968 if (buf[i] == '\n') {
1969 parse[parse_pos] = NULLCHAR;
1970 AppendComment(forwardMostMove, StripHighlight(parse));
1971 started = STARTED_NONE;
1973 /* Don't match patterns against characters in chatter */
1978 if (started == STARTED_CHATTER) {
1979 if (buf[i] != '\n') {
1980 /* Don't match patterns against characters in chatter */
1984 started = STARTED_NONE;
1987 /* Kludge to deal with rcmd protocol */
1988 if (firstTime && looking_at(buf, &i, "\001*")) {
1989 DisplayFatalError(&buf[1], 0, 1);
1995 if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
1998 if (appData.debugMode)
1999 fprintf(debugFP, "ics_type %d\n", ics_type);
2002 if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
2003 ics_type = ICS_FICS;
2005 if (appData.debugMode)
2006 fprintf(debugFP, "ics_type %d\n", ics_type);
2009 if (!loggedOn && looking_at(buf, &i, "chess.net")) {
2010 ics_type = ICS_CHESSNET;
2012 if (appData.debugMode)
2013 fprintf(debugFP, "ics_type %d\n", ics_type);
2018 (looking_at(buf, &i, "\"*\" is *a registered name") ||
2019 looking_at(buf, &i, "Logging you in as \"*\"") ||
2020 looking_at(buf, &i, "will be \"*\""))) {
2021 strcpy(ics_handle, star_match[0]);
2025 if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
2027 sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
2028 DisplayIcsInteractionTitle(buf);
2029 have_set_title = TRUE;
2032 /* skip finger notes */
2033 if (started == STARTED_NONE &&
2034 ((buf[i] == ' ' && isdigit(buf[i+1])) ||
2035 (buf[i] == '1' && buf[i+1] == '0')) &&
2036 buf[i+2] == ':' && buf[i+3] == ' ') {
2037 started = STARTED_CHATTER;
2042 /* skip formula vars */
2043 if (started == STARTED_NONE &&
2044 buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
2045 started = STARTED_CHATTER;
2051 /* save position of char array pointer for zippy */
2053 /* Try found special things that never works with color */
2054 /* I really don't know why - code is ok. */
2057 if (appData.zippyTalk || appData.zippyPlay) {
2058 if (looking_at(buf, &save, "* tells you: *") ||
2059 looking_at(buf, &save, "* says: *")) {
2060 player = StripHighlightAndTitle(star_match[0]);
2061 if (appData.zippyPassword[0] != NULLCHAR &&
2062 strncmp(star_match[1], appData.zippyPassword,
2063 strlen(appData.zippyPassword)) == 0) {
2064 q = star_match[1] + strlen(appData.zippyPassword);
2065 while (*q == ' ') q++;
2066 fprintf(debugFP, "zippy 1 %s \n", q);
2069 } else if(appData.zippyPassword2[0] != NULLCHAR && first.initDone &&
2070 strncmp(star_match[1], appData.zippyPassword2,
2071 strlen(appData.zippyPassword2)) == 0) {
2072 fprintf(debugFP, "zippy2vor: %s \n", q);
2073 q = star_match[1] + strlen(appData.zippyPassword2);
2074 while (*q == ' ') q++;
2075 SendToProgram(q, &first);
2076 SendToProgram("\n", &first);
2078 } else if (appData.zippyPassword3[0] != NULLCHAR && first.initDone &&
2079 strncmp(star_match[1], appData.zippyPassword3,
2080 strlen(appData.zippyPassword3)) == 0 &&
2081 appData.userVersion == FALSE) {
2082 q = star_match[1] + strlen(appData.zippyPassword3);
2083 ParseZippyP3(q, player);
2085 } else if (strncmp(star_match[1], "showinfo", 8) == 0 &&
2086 appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
2087 appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
2088 q = star_match[1] + strlen("showinfo");
2089 showInfo(q, player);
2090 } else if (strncmp(star_match[1], "showgames", 9) == 0 &&
2091 appData.userVersion == FALSE && appData.icsAnalyze == TRUE &&
2092 appData.icsAnalyzeOutPut == 3 && appData.ICC_feature == TRUE) {
2094 showInfo(q, player);
2096 } else if (appData.zippyWrongPassword[0] != NULLCHAR &&
2097 strncmp(star_match[1], appData.zippyWrongPassword,
2098 strlen(appData.zippyWrongPassword)) == 0) {
2099 q = star_match[1] + strlen(appData.zippyWrongPassword);
2100 while (*q == ' ') q++;
2101 sprintf(reply, "wrong %s\n", player);
2105 /* workaround for icc and freechess.org */
2106 if (looking_at(buf, &save, "Your opponent offers you a draw") ||
2107 looking_at(buf, &save, "offers you a draw") ||
2108 looking_at(buf, &save, "* offers you a draw")) {
2109 if (first.sendDrawOffers && first.initDone) {
2110 SendToProgram("draw\n", &first);
2111 /* Handling zippy Draw */
2112 } else if (appData.zippyDraw && first.initDone) {
2113 //ZippyDraw(1, programStats.score, programStats.depth);
2116 if (appData.zippyPlay && first.initDone && loggedOn == TRUE) ZippyMatch(buf, &save, player);
2119 /* Make color for all and for zippy */
2120 if (/* Don't color "message" or "messages" output */
2121 (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
2122 looking_at(buf, &i, "*. * at *:*: ") ||
2123 looking_at(buf, &i, "--* (*:*): ") ||
2124 /* Regular tells and says */
2125 (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
2126 looking_at(buf, &i, "* (your partner) tells you: ") ||
2127 looking_at(buf, &i, "* says: ") ||
2128 /* Message notifications (same color as tells) */
2129 looking_at(buf, &i, "* has left a message ") ||
2130 looking_at(buf, &i, "* just sent you a message:\n") ||
2131 /* Whispers and kibitzes */
2132 (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
2133 looking_at(buf, &i, "* kibitzes: ") ||
2135 (tkind = 3, looking_at(buf, &i, "*(*: "))) {
2137 if (tkind == 1 && strchr(star_match[0], ':')) {
2138 /* Avoid "tells you:" spoofs in channels */
2141 if (star_match[0][0] == NULLCHAR ||
2142 strchr(star_match[0], ' ') ||
2143 (tkind == 3 && strchr(star_match[1], ' '))) {
2144 /* Reject bogus matches */
2147 if (appData.colorize) {
2148 if (oldi > next_out) {
2149 SendToPlayer(&buf[next_out], oldi - next_out);
2154 Colorize(ColorTell, FALSE);
2155 curColor = ColorTell;
2158 Colorize(ColorKibitz, FALSE);
2159 curColor = ColorKibitz;
2162 p = strrchr(star_match[1], '(');
2169 Colorize(ColorChannel1, FALSE);
2170 curColor = ColorChannel1;
2172 Colorize(ColorChannel, FALSE);
2173 curColor = ColorChannel;
2177 curColor = ColorNormal;
2181 if (started == STARTED_NONE && appData.autoComment &&
2182 (gameMode == IcsObserving ||
2183 gameMode == IcsPlayingWhite ||
2184 gameMode == IcsPlayingBlack)) {
2185 parse_pos = i - oldi;
2186 memcpy(parse, &buf[oldi], parse_pos);
2187 parse[parse_pos] = NULLCHAR;
2188 started = STARTED_COMMENT;
2189 savingComment = TRUE;
2191 started = STARTED_CHATTER;
2192 savingComment = FALSE;
2199 if (looking_at(buf, &i, "* s-shouts: ") ||
2200 looking_at(buf, &i, "* c-shouts: ")) {
2201 if (appData.colorize) {
2202 if (oldi > next_out) {
2203 SendToPlayer(&buf[next_out], oldi - next_out);
2206 Colorize(ColorSShout, FALSE);
2207 curColor = ColorSShout;
2210 started = STARTED_CHATTER;
2214 if (looking_at(buf, &i, "--->")) {
2219 if (looking_at(buf, &i, "* shouts: ") ||
2220 looking_at(buf, &i, "--> ")) {
2221 if (appData.colorize) {
2222 if (oldi > next_out) {
2223 SendToPlayer(&buf[next_out], oldi - next_out);
2226 Colorize(ColorShout, FALSE);
2227 curColor = ColorShout;
2230 started = STARTED_CHATTER;
2234 if (looking_at( buf, &i, "Challenge:")) {
2235 if (appData.colorize) {
2236 if (oldi > next_out) {
2237 SendToPlayer(&buf[next_out], oldi - next_out);
2240 Colorize(ColorChallenge, FALSE);
2241 curColor = ColorChallenge;
2247 if (looking_at(buf, &i, "* offers you") ||
2248 looking_at(buf, &i, "* offers to be") ||
2249 looking_at(buf, &i, "* would like to") ||
2250 looking_at(buf, &i, "* requests to") ||
2251 looking_at(buf, &i, "Your opponent offers") ||
2252 looking_at(buf, &i, "Your opponent requests")) {
2254 if (appData.colorize) {
2255 if (oldi > next_out) {
2256 SendToPlayer(&buf[next_out], oldi - next_out);
2259 Colorize(ColorRequest, FALSE);
2260 curColor = ColorRequest;
2265 if (looking_at(buf, &i, "* (*) seeking")) {
2266 if (appData.colorize) {
2267 if (oldi > next_out) {
2268 SendToPlayer(&buf[next_out], oldi - next_out);
2271 Colorize(ColorSeek, FALSE);
2272 curColor = ColorSeek;
2278 if (looking_at(buf, &i, "\\ ")) {
2279 if (prevColor != ColorNormal) {
2280 if (oldi > next_out) {
2281 SendToPlayer(&buf[next_out], oldi - next_out);
2284 Colorize(prevColor, TRUE);
2285 curColor = prevColor;
2287 if (savingComment) {
2288 parse_pos = i - oldi;
2289 memcpy(parse, &buf[oldi], parse_pos);
2290 parse[parse_pos] = NULLCHAR;
2291 started = STARTED_COMMENT;
2293 started = STARTED_CHATTER;
2298 if (looking_at(buf, &i, "Black Strength :") ||
2299 looking_at(buf, &i, "<<< style 10 board >>>") ||
2300 looking_at(buf, &i, "<10>") ||
2301 looking_at(buf, &i, "#@#")) {
2302 /* Wrong board style */
2304 SendToICS(ics_prefix);
2305 SendToICS("set style 12\n");
2306 SendToICS(ics_prefix);
2307 SendToICS("refresh\n");
2311 if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
2313 have_sent_ICS_logon = 1;
2317 if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ &&
2318 (looking_at(buf, &i, "\n<12> ") ||
2319 looking_at(buf, &i, "<12> "))) {
2321 if (oldi > next_out) {
2322 SendToPlayer(&buf[next_out], oldi - next_out);
2325 started = STARTED_BOARD;
2330 if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
2331 looking_at(buf, &i, "<b1> ")) {
2332 if (oldi > next_out) {
2333 SendToPlayer(&buf[next_out], oldi - next_out);
2336 started = STARTED_HOLDINGS;
2341 /* Send buf now to zippy */
2342 if (appData.zippyTalk || appData.zippyPlay) {
2344 if (ZippyControl(buf, &save) || ZippyConverse(buf, &save)) {
2351 /* ICS: init icsQueue */
2352 if (appData.zippyPlay && first.initDone && gameMode == IcsObserving) {
2353 if (ics_gamenum > max_gamenum || ics_gamenum == -1) {
2354 if (appData.debugMode) fprintf(debugFP, "To high gamenumber or gamenumber -1 !\n");
2357 if (icsQueue[ics_gamenum].killPv == 0) {
2358 icsQueue[ics_gamenum].move = currentMove;
2359 icsQueue[ics_gamenum].killPv = appData.icsKillPVs;
2360 icsQueue[ics_gamenum].counter = 0;
2361 strcpy(icsQueue[ics_gamenum].white, gameInfo.white);
2362 strcpy(icsQueue[ics_gamenum].black, gameInfo.black);
2366 if (looking_at(buf, &i, "* *vs. * *--- *")) {
2368 /* Header for a move list -- first line */
2369 switch (ics_getting_history) {
2373 case BeginningOfGame:
2374 /* User typed "moves" or "oldmoves" while we
2375 were idle. Pretend we asked for these
2376 moves and soak them up so user can step
2377 through them and/or save them.
2380 gameMode = IcsObserving;
2383 ics_getting_history = H_GOT_UNREQ_HEADER;
2385 case EditGame: /*?*/
2386 case EditPosition: /*?*/
2387 /* Should above feature work in these modes too? */
2388 /* For now it doesn't */
2389 ics_getting_history = H_GOT_UNWANTED_HEADER;
2392 ics_getting_history = H_GOT_UNWANTED_HEADER;
2397 /* Is this the right one? */
2398 if (gameInfo.white && gameInfo.black &&
2399 strcmp(gameInfo.white, star_match[0]) == 0 &&
2400 strcmp(gameInfo.black, star_match[2]) == 0) {
2402 ics_getting_history = H_GOT_REQ_HEADER;
2405 case H_GOT_REQ_HEADER:
2406 case H_GOT_UNREQ_HEADER:
2407 case H_GOT_UNWANTED_HEADER:
2408 case H_GETTING_MOVES:
2409 /* Should not happen */
2410 DisplayError("Error gathering move list: two headers", 0);
2411 ics_getting_history = H_FALSE;
2415 /* Save player ratings into gameInfo if needed */
2416 if ((ics_getting_history == H_GOT_REQ_HEADER ||
2417 ics_getting_history == H_GOT_UNREQ_HEADER) &&
2418 (gameInfo.whiteRating == -1 ||
2419 gameInfo.blackRating == -1)) {
2421 gameInfo.whiteRating = string_to_rating(star_match[1]);
2422 gameInfo.blackRating = string_to_rating(star_match[3]);
2423 if (appData.debugMode)
2424 fprintf(debugFP, "Ratings from header: W %d, B %d\n",
2425 gameInfo.whiteRating, gameInfo.blackRating);
2430 if (looking_at(buf, &i,
2431 "* * match, initial time: * minute*, increment: * second")) {
2432 /* Header for a move list -- second line */
2433 /* Initial board will follow if this is a wild game */
2435 if (gameInfo.event != NULL) free(gameInfo.event);
2436 sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
2437 gameInfo.event = StrSave(str);
2438 gameInfo.variant = StringToVariant(gameInfo.event);
2442 if (looking_at(buf, &i, "Move ")) {
2443 /* Beginning of a move list */
2444 switch (ics_getting_history) {
2446 /* Normally should not happen */
2447 /* Maybe user hit reset while we were parsing */
2450 /* Happens if we are ignoring a move list that is not
2451 * the one we just requested. Common if the user
2452 * tries to observe two games without turning off
2455 case H_GETTING_MOVES:
2456 /* Should not happen */
2457 DisplayError("Error gathering move list: nested", 0);
2458 ics_getting_history = H_FALSE;
2460 case H_GOT_REQ_HEADER:
2461 ics_getting_history = H_GETTING_MOVES;
2462 started = STARTED_MOVES;
2464 if (oldi > next_out) {
2465 SendToPlayer(&buf[next_out], oldi - next_out);
2468 case H_GOT_UNREQ_HEADER:
2469 ics_getting_history = H_GETTING_MOVES;
2470 started = STARTED_MOVES_NOHIDE;
2473 case H_GOT_UNWANTED_HEADER:
2474 ics_getting_history = H_FALSE;
2480 if (looking_at(buf, &i, "% ") ||
2481 ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
2482 && looking_at(buf, &i, "}*"))) {
2483 savingComment = FALSE;
2486 case STARTED_MOVES_NOHIDE:
2487 memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
2488 parse[parse_pos + i - oldi] = NULLCHAR;
2489 ParseGameHistory(parse);
2491 if (appData.zippyPlay && first.initDone) {
2492 if (appData.icsAnalyze && gameMode == IcsObserving) {
2495 /* icsAnalyze send the moves to engine if we start new */
2496 if (!appData.icsAnalyze) FeedMovesToProgram(&first, forwardMostMove);
2497 /* Bug 4.2.6: Engine want play, skip that */
2498 if (gameMode == IcsExamining) {
2500 SendToProgram("force\n", &first);
2502 if (gameMode == IcsPlayingWhite) {
2503 if (appData.icsAnalyze) {
2505 appData.icsAnalyze = FALSE;
2508 if (WhiteOnMove(forwardMostMove)) {
2509 if (first.sendTime) {
2510 if (first.useColors) {
2511 SendToProgram("black\n", &first);
2513 SendTimeRemaining(&first, TRUE);
2515 if (first.useColors) {
2516 SendToProgram("white\ngo\n", &first);
2518 SendToProgram("go\n", &first);
2520 first.maybeThinking = TRUE;
2522 if (first.usePlayother) {
2523 if (first.sendTime) {
2524 SendTimeRemaining(&first, TRUE);
2526 SendToProgram("playother\n", &first);
2532 } else if (gameMode == IcsPlayingBlack) {
2533 if (appData.icsAnalyze) {
2535 appData.icsAnalyze = FALSE;
2536 SendToICS("refresh\n");
2538 /* engineRoom stay forever */
2539 if (!WhiteOnMove(forwardMostMove)) {
2540 if (first.sendTime) {
2541 if (first.useColors) {
2542 SendToProgram("white\n", &first);
2544 SendTimeRemaining(&first, FALSE);
2546 if (first.useColors) {
2547 SendToProgram("black\ngo\n", &first);
2549 SendToProgram("go\n", &first);
2551 first.maybeThinking = TRUE;
2553 if (first.usePlayother) {
2554 if (first.sendTime) {
2555 SendTimeRemaining(&first, FALSE);
2557 SendToProgram("playother\n", &first);
2568 if (gameMode == IcsObserving && ics_gamenum == -1) {
2569 /* Moves came from oldmoves or moves command
2570 while we weren't doing anything else.
2572 currentMove = forwardMostMove;
2573 ClearHighlights();/*!!could figure this out*/
2574 flipView = appData.flipView;
2575 DrawPosition(FALSE, boards[currentMove]);
2576 DisplayBothClocks();
2577 sprintf(str, "%s vs. %s",
2578 gameInfo.white, gameInfo.black);
2582 /* Moves were history of an active game */
2583 if (gameInfo.resultDetails != NULL) {
2584 free(gameInfo.resultDetails);
2585 gameInfo.resultDetails = NULL;
2588 HistorySet(parseList, backwardMostMove,
2589 forwardMostMove, currentMove-1);
2590 DisplayMove(currentMove - 1);
2591 if (started == STARTED_MOVES) next_out = i;
2592 started = STARTED_NONE;
2593 ics_getting_history = H_FALSE;
2596 case STARTED_OBSERVE:
2597 started = STARTED_NONE;
2598 SendToICS(ics_prefix);
2599 SendToICS("refresh\n");
2608 if ((started == STARTED_MOVES || started == STARTED_BOARD ||
2609 started == STARTED_HOLDINGS ||
2610 started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
2611 /* Accumulate characters in move list or board */
2612 parse[parse_pos++] = buf[i];
2615 /* Start of game messages. Mostly we detect start of game
2616 when the first board image arrives. On some versions
2617 of the ICS, though, we need to do a "refresh" after starting
2618 to observe in order to get the current board right away. */
2619 if (looking_at(buf, &i, "Adding game * to observation list")) {
2620 started = STARTED_OBSERVE;
2624 /* Handle auto-observe */
2625 if (appData.autoObserve &&
2626 (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
2627 looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
2629 /* Choose the player that was highlighted, if any. */
2630 if (star_match[0][0] == '\033' ||
2631 star_match[1][0] != '\033') {
2632 player = star_match[0];
2634 player = star_match[2];
2636 sprintf(str, "%sobserve %s\n",
2637 ics_prefix, StripHighlightAndTitle(player));
2640 /* Save ratings from notify string */
2641 strcpy(player1Name, star_match[0]);
2642 player1Rating = string_to_rating(star_match[1]);
2643 strcpy(player2Name, star_match[2]);
2644 player2Rating = string_to_rating(star_match[3]);
2646 if (appData.debugMode)
2648 "Ratings from 'Game notification:' %s %d, %s %d\n",
2649 player1Name, player1Rating,
2650 player2Name, player2Rating);
2655 /* Deal with automatic examine mode after a game,
2656 and with IcsObserving -> IcsExamining transition */
2657 if (looking_at(buf, &i, "Entering examine mode for game *") ||
2658 looking_at(buf, &i, "has made you an examiner of game *")) {
2660 int gamenum = atoi(star_match[0]);
2661 if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
2662 gamenum == ics_gamenum) {
2663 /* We were already playing or observing this game;
2664 no need to refetch history */
2665 gameMode = IcsExamining;
2667 pauseExamForwardMostMove = forwardMostMove;
2668 } else if (currentMove < forwardMostMove) {
2669 ForwardInner(forwardMostMove);
2672 /* I don't think this case really can happen */
2673 SendToICS(ics_prefix);
2674 SendToICS("refresh\n");
2679 /* Error messages */
2680 if (ics_user_moved) {
2681 if (looking_at(buf, &i, "Illegal move") ||
2682 looking_at(buf, &i, "Not a legal move") ||
2683 looking_at(buf, &i, "Your king is in check") ||
2684 looking_at(buf, &i, "It isn't your turn") ||
2685 looking_at(buf, &i, "It is not your move")) {
2688 if (forwardMostMove > backwardMostMove) {
2689 currentMove = --forwardMostMove;
2690 DisplayMove(currentMove - 1); /* before DMError */
2691 DisplayMoveError("Illegal move (rejected by ICS)");
2692 DrawPosition(FALSE, boards[currentMove]);
2694 DisplayBothClocks();
2700 if (looking_at(buf, &i, "still have time") ||
2701 looking_at(buf, &i, "not out of time") ||
2702 looking_at(buf, &i, "either player is out of time") ||
2703 looking_at(buf, &i, "has timeseal; checking")) {
2704 /* We must have called his flag a little too soon */
2705 whiteFlag = blackFlag = FALSE;
2709 if (looking_at(buf, &i, "added * seconds to") ||
2710 looking_at(buf, &i, "seconds were added to")) {
2711 /* Update the clocks */
2712 SendToICS(ics_prefix);
2713 SendToICS("refresh\n");
2717 if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
2718 ics_clock_paused = TRUE;
2723 if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
2724 ics_clock_paused = FALSE;
2729 /* Grab player ratings from the Creating: message.
2730 Note we have to check for the special case when
2731 the ICS inserts things like [white] or [black]. */
2732 if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
2733 looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
2735 0 player 1 name (not necessarily white)
2737 2 empty, white, or black (IGNORED)
2738 3 player 2 name (not necessarily black)
2741 The names/ratings are sorted out when the game
2742 actually starts (below).
2744 strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
2745 player1Rating = string_to_rating(star_match[1]);
2746 strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
2747 player2Rating = string_to_rating(star_match[4]);
2749 if (appData.debugMode)
2751 "Ratings from 'Creating:' %s %d, %s %d\n",
2752 player1Name, player1Rating,
2753 player2Name, player2Rating);
2758 /* Improved generic start/end-of-game messages */
2759 if (looking_at(buf, &i, "{Game * (* vs. *) *}*")) {
2760 /* star_match[0] is the game number */
2761 /* [1] is the white player's name */
2762 /* [2] is the black player's name */
2763 /* For end-of-game: */
2764 /* [3] is the reason for the game end */
2765 /* [4] is a PGN end game-token, preceded by " " */
2766 /* For start-of-game: */
2767 /* [3] begins with "Creating" or "Continuing" */
2768 /* [4] is " *" or empty (don't care). */
2769 int gamenum = atoi(star_match[0]);
2770 char *why = star_match[3];
2771 char *endtoken = star_match[4];
2772 ChessMove endtype = (ChessMove) 0;
2774 /* Game start messages */
2775 if (strncmp(why, "Creating ", 9) == 0 ||
2776 strncmp(why, "Continuing ", 11) == 0) {
2777 gs_gamenum = gamenum;
2778 strcpy(gs_kind, strchr(why, ' ') + 1);
2780 if (appData.zippyPlay) {
2781 ZippyGameStart(star_match[1], star_match[2]);
2787 /* Game end messages */
2788 if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
2789 ics_gamenum != gamenum) {
2792 while (endtoken[0] == ' ') endtoken++;
2793 switch (endtoken[0]) {
2796 endtype = GameUnfinished;
2799 endtype = BlackWins;
2802 if (endtoken[1] == '/')
2803 endtype = GameIsDrawn;
2805 endtype = WhiteWins;
2808 GameEnds(endtype, why, GE_ICS);
2810 if (appData.zippyPlay && first.initDone) {
2811 ZippyGameEnd(endtype, why);
2812 if (first.pr == NULL) {
2813 /* Start the next process early so that we'll
2814 be ready for the next challenge */
2815 StartChessProgram(&first);
2817 /* Send "new" early, in case this command takes
2818 a long time to finish, so that we'll be ready
2819 for the next challenge. */
2826 if (looking_at(buf, &i, "Removing game * from observation") ||
2827 looking_at(buf, &i, "no longer observing game *") ||
2828 looking_at(buf, &i, "Game * (*) has no examiners")) {
2829 if (gameMode == IcsObserving &&
2830 atoi(star_match[0]) == ics_gamenum)
2832 if (appData.icsAnalyze) IcsAnalyze(FALSE);
2833 ResetIcsQueue(ics_gamenum);
2837 ics_user_moved = FALSE;
2842 if (looking_at(buf, &i, "no longer examining game *")) {
2843 if (gameMode == IcsExamining &&
2844 atoi(star_match[0]) == ics_gamenum)
2846 if (appData.icsAnalyze) IcsAnalyze(FALSE);
2847 ResetIcsQueue(ics_gamenum);
2850 ics_user_moved = FALSE;
2855 /* Advance leftover_start past any newlines we find,
2856 so only partial lines can get reparsed */
2857 if (looking_at(buf, &i, "\n")) {
2858 prevColor = curColor;
2859 if (curColor != ColorNormal) {
2860 if (oldi > next_out) {
2861 SendToPlayer(&buf[next_out], oldi - next_out);
2864 Colorize(ColorNormal, FALSE);
2865 curColor = ColorNormal;
2867 if (started == STARTED_BOARD) {
2868 started = STARTED_NONE;
2869 parse[parse_pos] = NULLCHAR;
2870 ParseBoard12(parse);
2873 /* Send premove here */
2874 if (appData.premove) {
2876 if (currentMove == 0 &&
2877 gameMode == IcsPlayingWhite &&
2878 appData.premoveWhite) {
2879 sprintf(str, "%s%s\n", ics_prefix,
2880 appData.premoveWhiteText);
2881 if (appData.debugMode)
2882 fprintf(debugFP, "Sending premove:\n");
2884 } else if (currentMove == 1 &&
2885 gameMode == IcsPlayingBlack &&
2886 appData.premoveBlack) {
2887 sprintf(str, "%s%s\n", ics_prefix,
2888 appData.premoveBlackText);
2889 if (appData.debugMode)
2890 fprintf(debugFP, "Sending premove:\n");
2892 } else if (gotPremove) {
2894 ClearPremoveHighlights();
2895 if (appData.debugMode)
2896 fprintf(debugFP, "Sending premove:\n");
2897 UserMoveEvent(premoveFromX, premoveFromY,
2898 premoveToX, premoveToY,
2903 /* Usually suppress following prompt */
2904 if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
2905 if (looking_at(buf, &i, "*% ")) {
2906 savingComment = FALSE;
2910 } else if (started == STARTED_HOLDINGS) {
2912 char new_piece[MSG_SIZ];
2913 started = STARTED_NONE;
2914 parse[parse_pos] = NULLCHAR;
2915 if (appData.debugMode)
2916 fprintf(debugFP, "Parsing holdings: %s\n", parse);
2917 if (sscanf(parse, " game %d", &gamenum) == 1 &&
2918 gamenum == ics_gamenum) {
2919 if (gameInfo.variant == VariantNormal) {
2920 gameInfo.variant = VariantCrazyhouse; /*temp guess*/
2921 /* Get a move list just to see the header, which
2922 will tell us whether this is really bug or zh */
2923 if (ics_getting_history == H_FALSE) {
2924 ics_getting_history = H_REQUESTED;
2925 sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
2929 new_piece[0] = NULLCHAR;
2930 sscanf(parse, "game %d white [%s black [%s <- %s",
2931 &gamenum, white_holding, black_holding,
2933 white_holding[strlen(white_holding)-1] = NULLCHAR;
2934 black_holding[strlen(black_holding)-1] = NULLCHAR;
2936 if (appData.zippyPlay && first.initDone) {
2937 ZippyHoldings(white_holding, black_holding,
2941 if (tinyLayout || smallLayout) {
2942 char wh[16], bh[16];
2943 PackHolding(wh, white_holding);
2944 PackHolding(bh, black_holding);
2945 sprintf(str, "[%s-%s] %s-%s", wh, bh,
2946 gameInfo.white, gameInfo.black);
2948 sprintf(str, "%s [%s] vs. %s [%s]",
2949 gameInfo.white, white_holding,
2950 gameInfo.black, black_holding);
2952 DrawPosition(FALSE, NULL);
2955 /* Suppress following prompt */
2956 if (looking_at(buf, &i, "*% ")) {
2957 savingComment = FALSE;
2964 i++; /* skip unparsed character and loop back */
2967 if (started != STARTED_MOVES && started != STARTED_BOARD &&
2968 started != STARTED_HOLDINGS && i > next_out) {
2969 SendToPlayer(&buf[next_out], i - next_out);
2973 leftover_len = buf_len - leftover_start;
2974 /* if buffer ends with something we couldn't parse,
2975 reparse it after appending the next read */
2977 } else if (count == 0) {
2978 RemoveInputSource(isr);
2979 DisplayFatalError("Connection closed by ICS", 0, 0);
2981 DisplayFatalError("Error reading from ICS", error, 1);
2986 /* Board style 12 looks like this:
2988 <12> r-b---k- pp----pp ---bP--- ---p---- q------- ------P- P--Q--BP -----R-K W -1 0 0 0 0 0 0 paf MaxII 0 2 12 21 25 234 174 24 Q/d7-a4 (0:06) Qxa4 0 0