changes from Alessandro Scotti from 20051231
[xboard.git] / backend.c
1 /*
2  * backend.c -- Common back end for X and Windows NT versions of
3  * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $
4  *
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
7  *
8  * The following terms apply to Digital Equipment Corporation's copyright
9  * interest in XBoard:
10  * ------------------------------------------------------------------------
11  * All Rights Reserved
12  *
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.
20  *
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
27  * SOFTWARE.
28  * ------------------------------------------------------------------------
29  *
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.
37  *
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.
42  *
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  * ------------------------------------------------------------------------
47  *
48  * See the file ChangeLog for a revision history.  */
49
50 /* [AS] Also useful here for debugging */
51 #ifdef WIN32
52 #include <windows.h>
53
54 #define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );
55
56 #else
57
58 #define DoSleep( n )
59
60 #endif
61
62 #include "config.h"
63
64 #include <assert.h>
65 #include <stdio.h>
66 #include <ctype.h>
67 #include <errno.h>
68 #include <sys/types.h>
69 #include <sys/stat.h>
70 #include <math.h>
71
72 #if STDC_HEADERS
73 # include <stdlib.h>
74 # include <string.h>
75 #else /* not STDC_HEADERS */
76 # if HAVE_STRING_H
77 #  include <string.h>
78 # else /* not HAVE_STRING_H */
79 #  include <strings.h>
80 # endif /* not HAVE_STRING_H */
81 #endif /* not STDC_HEADERS */
82
83 #if HAVE_SYS_FCNTL_H
84 # include <sys/fcntl.h>
85 #else /* not HAVE_SYS_FCNTL_H */
86 # if HAVE_FCNTL_H
87 #  include <fcntl.h>
88 # endif /* HAVE_FCNTL_H */
89 #endif /* not HAVE_SYS_FCNTL_H */
90
91 #if TIME_WITH_SYS_TIME
92 # include <sys/time.h>
93 # include <time.h>
94 #else
95 # if HAVE_SYS_TIME_H
96 #  include <sys/time.h>
97 # else
98 #  include <time.h>
99 # endif
100 #endif
101
102 #if defined(_amigados) && !defined(__GNUC__)
103 struct timezone {
104     int tz_minuteswest;
105     int tz_dsttime;
106 };
107 extern int gettimeofday(struct timeval *, struct timezone *);
108 #endif
109
110 #if HAVE_UNISTD_H
111 # include <unistd.h>
112 #endif
113
114 #include "common.h"
115 #include "frontend.h"
116 #include "backend.h"
117 #include "parser.h"
118 #include "moves.h"
119 #if ZIPPY
120 # include "zippy.h"
121 #endif
122 #include "backendz.h"
123
124 /* A point in time */
125 typedef struct {
126     long sec;  /* Assuming this is >= 32 bits */
127     int ms;    /* Assuming this is >= 16 bits */
128 } TimeMark;
129
130 /* Search stats from chessprogram */
131 typedef struct {
132   char movelist[2*MSG_SIZ]; /* Last PV we were sent */
133   int depth;              /* Current search depth */
134   int nr_moves;           /* Total nr of root moves */
135   int moves_left;         /* Moves remaining to be searched */
136   char move_name[MOVE_LEN];  /* Current move being searched, if provided */
137   unsigned long nodes;    /* # of nodes searched */
138   int time;               /* Search time (centiseconds) */
139   int score;              /* Score (centipawns) */
140   int got_only_move;      /* If last msg was "(only move)" */
141   int got_fail;           /* 0 - nothing, 1 - got "--", 2 - got "++" */
142   int ok_to_send;         /* handshaking between send & recv */
143   int line_is_book;       /* 1 if movelist is book moves */
144   int seen_stat;          /* 1 if we've seen the stat01: line */
145 } ChessProgramStats;
146
147 int establish P((void));
148 void read_from_player P((InputSourceRef isr, VOIDSTAR closure,
149                          char *buf, int count, int error));
150 void read_from_ics P((InputSourceRef isr, VOIDSTAR closure,
151                       char *buf, int count, int error));
152 void SendToICS P((char *s));
153 void SendToICSDelayed P((char *s, long msdelay));
154 void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
155                       int toX, int toY));
156 void InitPosition P((int redraw));
157 void HandleMachineMove P((char *message, ChessProgramState *cps));
158 int AutoPlayOneMove P((void));
159 int LoadGameOneMove P((ChessMove readAhead));
160 int LoadGameFromFile P((char *filename, int n, char *title, int useList));
161 int LoadPositionFromFile P((char *filename, int n, char *title));
162 int SavePositionToFile P((char *filename));
163 void ApplyMove P((int fromX, int fromY, int toX, int toY, int promoChar,
164                   Board board));
165 void MakeMove P((int fromX, int fromY, int toX, int toY, int promoChar));
166 void ShowMove P((int fromX, int fromY, int toX, int toY));
167 void FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
168                    /*char*/int promoChar));
169 void BackwardInner P((int target));
170 void ForwardInner P((int target));
171 void GameEnds P((ChessMove result, char *resultDetails, int whosays));
172 void EditPositionDone P((void));
173 void PrintOpponents P((FILE *fp));
174 void PrintPosition P((FILE *fp, int move));
175 void StartChessProgram P((ChessProgramState *cps));
176 void SendToProgram P((char *message, ChessProgramState *cps));
177 void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
178 void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
179                            char *buf, int count, int error));
180 void SendTimeControl P((ChessProgramState *cps,
181                         int mps, long tc, int inc, int sd, int st));
182 char *TimeControlTagValue P((void));
183 void Attention P((ChessProgramState *cps));
184 void FeedMovesToProgram P((ChessProgramState *cps, int upto));
185 void ResurrectChessProgram P((void));
186 void DisplayComment P((int moveNumber, char *text));
187 void DisplayMove P((int moveNumber));
188 void DisplayAnalysis P((void));
189
190 void ParseGameHistory P((char *game));
191 void ParseBoard12 P((char *string));
192 void StartClocks P((void));
193 void SwitchClocks P((void));
194 void StopClocks P((void));
195 void ResetClocks P((void));
196 char *PGNDate P((void));
197 void SetGameInfo P((void));
198 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));
199 int RegisterMove P((void));
200 void MakeRegisteredMove P((void));
201 void TruncateGame P((void));
202 int looking_at P((char *, int *, char *));
203 void CopyPlayerNameIntoFileName P((char **, char *));
204 char *SavePart P((char *));
205 int SaveGameOldStyle P((FILE *));
206 int SaveGamePGN P((FILE *));
207 void GetTimeMark P((TimeMark *));
208 long SubtractTimeMarks P((TimeMark *, TimeMark *));
209 int CheckFlags P((void));
210 long NextTickLength P((long));
211 void CheckTimeControl P((void));
212 void show_bytes P((FILE *, char *, int));
213 int string_to_rating P((char *str));
214 void ParseFeatures P((char* args, ChessProgramState *cps));
215 void InitBackEnd3 P((void));
216 void FeatureDone P((ChessProgramState* cps, int val));
217 void InitChessProgram P((ChessProgramState *cps));
218
219 void GetInfoFromComment( int, char * );
220
221 extern int tinyLayout, smallLayout;
222 static ChessProgramStats programStats;
223
224 /* States for ics_getting_history */
225 #define H_FALSE 0
226 #define H_REQUESTED 1
227 #define H_GOT_REQ_HEADER 2
228 #define H_GOT_UNREQ_HEADER 3
229 #define H_GETTING_MOVES 4
230 #define H_GOT_UNWANTED_HEADER 5
231
232 /* whosays values for GameEnds */
233 #define GE_ICS 0
234 #define GE_ENGINE 1
235 #define GE_PLAYER 2
236 #define GE_FILE 3
237 #define GE_XBOARD 4
238
239 /* Maximum number of games in a cmail message */
240 #define CMAIL_MAX_GAMES 20
241
242 /* Different types of move when calling RegisterMove */
243 #define CMAIL_MOVE   0
244 #define CMAIL_RESIGN 1
245 #define CMAIL_DRAW   2
246 #define CMAIL_ACCEPT 3
247
248 /* Different types of result to remember for each game */
249 #define CMAIL_NOT_RESULT 0
250 #define CMAIL_OLD_RESULT 1
251 #define CMAIL_NEW_RESULT 2
252
253 /* Telnet protocol constants */
254 #define TN_WILL 0373
255 #define TN_WONT 0374
256 #define TN_DO   0375
257 #define TN_DONT 0376
258 #define TN_IAC  0377
259 #define TN_ECHO 0001
260 #define TN_SGA  0003
261 #define TN_PORT 23
262
263 /* [AS] */
264 static char * safeStrCpy( char * dst, const char * src, size_t count )
265 {
266     assert( dst != NULL );
267     assert( src != NULL );
268     assert( count > 0 );
269
270     strncpy( dst, src, count );
271     dst[ count-1 ] = '\0';
272     return dst;
273 }
274
275 static char * safeStrCat( char * dst, const char * src, size_t count )
276 {
277     size_t  dst_len;
278
279     assert( dst != NULL );
280     assert( src != NULL );
281     assert( count > 0 );
282
283     dst_len = strlen(dst);
284
285     assert( count > dst_len ); /* Buffer size must be greater than current length */
286
287     safeStrCpy( dst + dst_len, src, count - dst_len );
288
289     return dst;
290 }
291
292 /* Fake up flags for now, as we aren't keeping track of castling
293    availability yet */
294 int
295 PosFlags(index)
296 {
297   int flags = F_ALL_CASTLE_OK;
298   if ((index % 2) == 0) flags |= F_WHITE_ON_MOVE;
299   switch (gameInfo.variant) {
300   case VariantSuicide:
301   case VariantGiveaway:
302     flags |= F_IGNORE_CHECK;
303     flags &= ~F_ALL_CASTLE_OK;
304     break;
305   case VariantAtomic:
306     flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;
307     break;
308   case VariantKriegspiel:
309     flags |= F_KRIEGSPIEL_CAPTURE;
310     break;
311   case VariantNoCastle:
312     flags &= ~F_ALL_CASTLE_OK;
313     break;
314   default:
315     break;
316   }
317   return flags;
318 }
319
320 FILE *gameFileFP, *debugFP;
321
322 /*
323     [AS] Note: sometimes, the sscanf() function is used to parse the input
324     into a fixed-size buffer. Because of this, we must be prepared to
325     receive strings as long as the size of the input buffer, which is currently
326     set to 4K for Windows and 8K for the rest.
327     So, we must either allocate sufficiently large buffers here, or
328     reduce the size of the input buffer in the input reading part.
329 */
330
331 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
332 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
333 char thinkOutput1[MSG_SIZ*10];
334
335 ChessProgramState first, second;
336
337 /* premove variables */
338 int premoveToX = 0;
339 int premoveToY = 0;
340 int premoveFromX = 0;
341 int premoveFromY = 0;
342 int premovePromoChar = 0;
343 int gotPremove = 0;
344 Boolean alarmSounded;
345 /* end premove variables */
346
347 #define ICS_GENERIC 0
348 #define ICS_ICC 1
349 #define ICS_FICS 2
350 #define ICS_CHESSNET 3 /* not really supported */
351 int ics_type = ICS_GENERIC;
352 char *ics_prefix = "$";
353
354 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0;
355 int pauseExamForwardMostMove = 0;
356 int nCmailGames = 0, nCmailResults = 0, nCmailMovesRegistered = 0;
357 int cmailMoveRegistered[CMAIL_MAX_GAMES], cmailResult[CMAIL_MAX_GAMES];
358 int cmailMsgLoaded = FALSE, cmailMailedMove = FALSE;
359 int cmailOldMove = -1, firstMove = TRUE, flipView = FALSE;
360 int blackPlaysFirst = FALSE, startedFromSetupPosition = FALSE;
361 int searchTime = 0, pausing = FALSE, pauseExamInvalid = FALSE;
362 int whiteFlag = FALSE, blackFlag = FALSE;
363 int userOfferedDraw = FALSE;
364 int ics_user_moved = 0, ics_gamenum = -1, ics_getting_history = H_FALSE;
365 int matchMode = FALSE, hintRequested = FALSE, bookRequested = FALSE;
366 int cmailMoveType[CMAIL_MAX_GAMES];
367 long ics_clock_paused = 0;
368 ProcRef icsPR = NoProc, cmailPR = NoProc;
369 InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
370 GameMode gameMode = BeginningOfGame;
371 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
372 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
373 ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */
374 int hiddenThinkOutputState = 0; /* [AS] */
375 int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */
376 int adjudicateLossPlies = 6;
377 char white_holding[64], black_holding[64];
378 TimeMark lastNodeCountTime;
379 long lastNodeCount=0;
380 int have_sent_ICS_logon = 0;
381 int movesPerSession;
382 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
383 long timeControl_2; /* [AS] Allow separate time controls */
384 long timeRemaining[2][MAX_MOVES];
385 int matchGame = 0;
386 TimeMark programStartTime;
387 char ics_handle[MSG_SIZ];
388 int have_set_title = 0;
389
390 /* animateTraining preserves the state of appData.animate
391  * when Training mode is activated. This allows the
392  * response to be animated when appData.animate == TRUE and
393  * appData.animateDragging == TRUE.
394  */
395 Boolean animateTraining;
396
397 GameInfo gameInfo;
398
399 AppData appData;
400
401 Board boards[MAX_MOVES];
402 Board initialPosition = {
403     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
404         WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
405     { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
406         WhitePawn, WhitePawn, WhitePawn, WhitePawn },
407     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
408         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
409     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
410         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
411     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
412         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
413     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
414         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
415     { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
416         BlackPawn, BlackPawn, BlackPawn, BlackPawn },
417     { BlackRook, BlackKnight, BlackBishop, BlackQueen,
418         BlackKing, BlackBishop, BlackKnight, BlackRook }
419 };
420 Board twoKingsPosition = {
421     { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
422         WhiteKing, WhiteKing, WhiteKnight, WhiteRook },
423     { WhitePawn, WhitePawn, WhitePawn, WhitePawn,
424         WhitePawn, WhitePawn, WhitePawn, WhitePawn },
425     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
426         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
427     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
428         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
429     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
430         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
431     { EmptySquare, EmptySquare, EmptySquare, EmptySquare,
432         EmptySquare, EmptySquare, EmptySquare, EmptySquare },
433     { BlackPawn, BlackPawn, BlackPawn, BlackPawn,
434         BlackPawn, BlackPawn, BlackPawn, BlackPawn },
435     { BlackRook, BlackKnight, BlackBishop, BlackQueen,
436         BlackKing, BlackKing, BlackKnight, BlackRook }
437 };
438
439
440 /* Convert str to a rating. Checks for special cases of "----",
441    "++++", etc. Also strips ()'s */
442 int
443 string_to_rating(str)
444   char *str;
445 {
446   while(*str && !isdigit(*str)) ++str;
447   if (!*str)
448     return 0;   /* One of the special "no rating" cases */
449   else
450     return atoi(str);
451 }
452
453 void
454 ClearProgramStats()
455 {
456     /* Init programStats */
457     programStats.movelist[0] = 0;
458     programStats.depth = 0;
459     programStats.nr_moves = 0;
460     programStats.moves_left = 0;
461     programStats.nodes = 0;
462     programStats.time = 100;
463     programStats.score = 0;
464     programStats.got_only_move = 0;
465     programStats.got_fail = 0;
466     programStats.line_is_book = 0;
467 }
468
469 void
470 InitBackEnd1()
471 {
472     int matched, min, sec;
473
474     GetTimeMark(&programStartTime);
475
476     ClearProgramStats();
477     programStats.ok_to_send = 1;
478     programStats.seen_stat = 0;
479
480     /*
481      * Initialize game list
482      */
483     ListNew(&gameList);
484
485
486     /*
487      * Internet chess server status
488      */
489     if (appData.icsActive) {
490         appData.matchMode = FALSE;
491         appData.matchGames = 0;
492 #if ZIPPY       
493         appData.noChessProgram = !appData.zippyPlay;
494 #else
495         appData.zippyPlay = FALSE;
496         appData.zippyTalk = FALSE;
497         appData.noChessProgram = TRUE;
498 #endif
499         if (*appData.icsHelper != NULLCHAR) {
500             appData.useTelnet = TRUE;
501             appData.telnetProgram = appData.icsHelper;
502         }
503     } else {
504         appData.zippyTalk = appData.zippyPlay = FALSE;
505     }
506
507     /* [AS] Initialize pv info list */
508     {
509         int i;
510
511         for( i=0; i<MAX_MOVES; i++ ) {
512             pvInfoList[i].depth = 0;
513         }
514     }
515
516     /*
517      * Parse timeControl resource
518      */
519     if (!ParseTimeControl(appData.timeControl, appData.timeIncrement,
520                           appData.movesPerSession)) {
521         char buf[MSG_SIZ];
522         sprintf(buf, "bad timeControl option %s", appData.timeControl);
523         DisplayFatalError(buf, 0, 2);
524     }
525
526     /*
527      * Parse searchTime resource
528      */
529     if (*appData.searchTime != NULLCHAR) {
530         matched = sscanf(appData.searchTime, "%d:%d", &min, &sec);
531         if (matched == 1) {
532             searchTime = min * 60;
533         } else if (matched == 2) {
534             searchTime = min * 60 + sec;
535         } else {
536             char buf[MSG_SIZ];
537             sprintf(buf, "bad searchTime option %s", appData.searchTime);
538             DisplayFatalError(buf, 0, 2);
539         }
540     }
541     
542     /* [AS] Adjudication threshold */
543     adjudicateLossThreshold = appData.adjudicateLossThreshold;
544
545     first.which = "first";
546     second.which = "second";
547     first.maybeThinking = second.maybeThinking = FALSE;
548     first.pr = second.pr = NoProc;
549     first.isr = second.isr = NULL;
550     first.sendTime = second.sendTime = 2;
551     first.sendDrawOffers = 1;
552     if (appData.firstPlaysBlack) {
553         first.twoMachinesColor = "black\n";
554         second.twoMachinesColor = "white\n";
555     } else {
556         first.twoMachinesColor = "white\n";
557         second.twoMachinesColor = "black\n";
558     }
559     first.program = appData.firstChessProgram;
560     second.program = appData.secondChessProgram;
561     first.host = appData.firstHost;
562     second.host = appData.secondHost;
563     first.dir = appData.firstDirectory;
564     second.dir = appData.secondDirectory;
565     first.other = &second;
566     second.other = &first;
567     first.initString = appData.initString;
568     second.initString = appData.secondInitString;
569     first.computerString = appData.firstComputerString;
570     second.computerString = appData.secondComputerString;
571     first.useSigint = second.useSigint = TRUE;
572     first.useSigterm = second.useSigterm = TRUE;
573     first.reuse = appData.reuseFirst;
574     second.reuse = appData.reuseSecond;
575     first.useSetboard = second.useSetboard = FALSE;
576     first.useSAN = second.useSAN = FALSE;
577     first.usePing = second.usePing = FALSE;
578     first.lastPing = second.lastPing = 0;
579     first.lastPong = second.lastPong = 0;
580     first.usePlayother = second.usePlayother = FALSE;
581     first.useColors = second.useColors = TRUE;
582     first.useUsermove = second.useUsermove = FALSE;
583     first.sendICS = second.sendICS = FALSE;
584     first.sendName = second.sendName = appData.icsActive;
585     first.sdKludge = second.sdKludge = FALSE;
586     first.stKludge = second.stKludge = FALSE;
587     TidyProgramName(first.program, first.host, first.tidy);
588     TidyProgramName(second.program, second.host, second.tidy);
589     first.matchWins = second.matchWins = 0;
590     strcpy(first.variants, appData.variant);
591     strcpy(second.variants, appData.variant);
592     first.analysisSupport = second.analysisSupport = 2; /* detect */
593     first.analyzing = second.analyzing = FALSE;
594     first.initDone = second.initDone = FALSE;
595
596     /* New features added by Tord: */
597     first.useFEN960 = FALSE; second.useFEN960 = FALSE;
598     first.useOOCastle = TRUE; second.useOOCastle = TRUE;
599     /* End of new features added by Tord. */
600     first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */
601     second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */
602
603     if (appData.firstProtocolVersion > PROTOVER ||
604         appData.firstProtocolVersion < 1) {
605       char buf[MSG_SIZ];
606       sprintf(buf, "protocol version %d not supported",
607               appData.firstProtocolVersion);
608       DisplayFatalError(buf, 0, 2);
609     } else {
610       first.protocolVersion = appData.firstProtocolVersion;
611     }
612
613     if (appData.secondProtocolVersion > PROTOVER ||
614         appData.secondProtocolVersion < 1) {
615       char buf[MSG_SIZ];
616       sprintf(buf, "protocol version %d not supported",
617               appData.secondProtocolVersion);
618       DisplayFatalError(buf, 0, 2);
619     } else {
620       second.protocolVersion = appData.secondProtocolVersion;
621     }
622
623     if (appData.icsActive) {
624         appData.clockMode = TRUE;  /* changes dynamically in ICS mode */
625     } else if (*appData.searchTime != NULLCHAR || appData.noChessProgram) {
626         appData.clockMode = FALSE;
627         first.sendTime = second.sendTime = 0;
628     }
629     
630 #if ZIPPY
631     /* Override some settings from environment variables, for backward
632        compatibility.  Unfortunately it's not feasible to have the env
633        vars just set defaults, at least in xboard.  Ugh.
634     */
635     if (appData.icsActive && (appData.zippyPlay || appData.zippyTalk)) {
636       ZippyInit();
637     }
638 #endif
639     
640     if (appData.noChessProgram) {
641         programVersion = (char*) malloc(5 + strlen(PRODUCT) + strlen(VERSION)
642                                         + strlen(PATCHLEVEL));
643         sprintf(programVersion, "%s %s.%s", PRODUCT, VERSION, PATCHLEVEL);
644     } else {
645         char *p, *q;
646         q = first.program;
647         while (*q != ' ' && *q != NULLCHAR) q++;
648         p = q;
649         while (p > first.program && *(p-1) != '/') p--;
650         programVersion = (char*) malloc(8 + strlen(PRODUCT) + strlen(VERSION)
651                                         + strlen(PATCHLEVEL) + (q - p));
652         sprintf(programVersion, "%s %s.%s + ", PRODUCT, VERSION, PATCHLEVEL);
653         strncat(programVersion, p, q - p);
654     }
655
656     if (!appData.icsActive) {
657       char buf[MSG_SIZ];
658       /* Check for variants that are supported only in ICS mode,
659          or not at all.  Some that are accepted here nevertheless
660          have bugs; see comments below.
661       */
662       VariantClass variant = StringToVariant(appData.variant);
663       switch (variant) {
664       case VariantBughouse:     /* need four players and two boards */
665       case VariantKriegspiel:   /* need to hide pieces and move details */
666       /* case VariantFischeRandom: (Fabien: moved below) */
667         sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);
668         DisplayFatalError(buf, 0, 2);
669         return;
670
671       case VariantUnknown:
672       case VariantLoadable:
673       case Variant29:
674       case Variant30:
675       case Variant31:
676       case Variant32:
677       case Variant33:
678       case Variant34:
679       case Variant35:
680       case Variant36:
681       default:
682         sprintf(buf, "Unknown variant name %s", appData.variant);
683         DisplayFatalError(buf, 0, 2);
684         return;
685
686       case VariantNormal:     /* definitely works! */
687       case VariantWildCastle: /* pieces not automatically shuffled */
688       case VariantNoCastle:   /* pieces not automatically shuffled */
689       case VariantFischeRandom: /* Fabien: pieces not automatically shuffled */
690       case VariantCrazyhouse: /* holdings not shown,
691                                  offboard interposition not understood */
692       case VariantLosers:     /* should work except for win condition,
693                                  and doesn't know captures are mandatory */
694       case VariantSuicide:    /* should work except for win condition,
695                                  and doesn't know captures are mandatory */
696       case VariantGiveaway:   /* should work except for win condition,
697                                  and doesn't know captures are mandatory */
698       case VariantTwoKings:   /* should work */
699       case VariantAtomic:     /* should work except for win condition */
700       case Variant3Check:     /* should work except for win condition */
701       case VariantShatranj:   /* might work if TestLegality is off */
702         break;
703       }
704     }
705 }
706
707 int NextIntegerFromString( char ** str, long * value )
708 {
709     int result = -1;
710     char * s = *str;
711
712     while( *s == ' ' || *s == '\t' ) {
713         s++;
714     }
715
716     *value = 0;
717
718     if( *s >= '0' && *s <= '9' ) {
719         while( *s >= '0' && *s <= '9' ) {
720             *value = *value * 10 + (*s - '0');
721             s++;
722         }
723
724         result = 0;
725     }
726
727     *str = s;
728
729     return result;
730 }
731
732 int NextTimeControlFromString( char ** str, long * value )
733 {
734     long temp;
735     int result = NextIntegerFromString( str, &temp );
736
737     if( result == 0 ) {
738         *value = temp * 60; /* Minutes */
739         if( **str == ':' ) {
740             (*str)++;
741             result = NextIntegerFromString( str, &temp );
742             *value += temp; /* Seconds */
743         }
744     }
745
746     return result;
747 }
748
749 int GetTimeControlForWhite()
750 {
751     int result = timeControl;
752
753     return result;
754 }
755
756 int GetTimeControlForBlack()
757 {
758     int result = timeControl;
759
760     if( timeControl_2 > 0 ) {
761         result = timeControl_2;
762     }
763
764     return result;
765 }
766
767 int
768 ParseTimeControl(tc, ti, mps)
769      char *tc;
770      int ti;
771      int mps;
772 {
773 #if 0
774     int matched, min, sec;
775
776     matched = sscanf(tc, "%d:%d", &min, &sec);
777     if (matched == 1) {
778         timeControl = min * 60 * 1000;
779     } else if (matched == 2) {
780         timeControl = (min * 60 + sec) * 1000;
781     } else {
782         return FALSE;
783     }
784 #else
785     long tc1;
786     long tc2;
787
788     if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
789         return FALSE;
790     }
791
792     if( *tc == '/' ) {
793         /* Parse second time control */
794         tc++;
795
796         if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
797             return FALSE;
798         }
799
800         if( tc2 == 0 ) {
801             return FALSE;
802         }
803
804         timeControl_2 = tc2 * 1000;
805     }
806     else {
807         timeControl_2 = 0;
808     }
809
810     if( tc1 == 0 ) {
811         return FALSE;
812     }
813
814     timeControl = tc1 * 1000;
815 #endif
816
817     if (ti >= 0) {
818         timeIncrement = ti * 1000;  /* convert to ms */
819         movesPerSession = 0;
820     } else {
821         timeIncrement = 0;
822         movesPerSession = mps;
823     }
824     return TRUE;
825 }
826
827 void
828 InitBackEnd2()
829 {
830     if (appData.debugMode) {
831         fprintf(debugFP, "%s\n", programVersion);
832     }
833
834     if (appData.matchGames > 0) {
835         appData.matchMode = TRUE;
836     } else if (appData.matchMode) {
837         appData.matchGames = 1;
838     }
839     Reset(TRUE, FALSE);
840     if (appData.noChessProgram || first.protocolVersion == 1) {
841       InitBackEnd3();
842     } else {
843       /* kludge: allow timeout for initial "feature" commands */
844       FreezeUI();
845       DisplayMessage("", "Starting chess program");
846       ScheduleDelayedEvent(InitBackEnd3, FEATURE_TIMEOUT);
847     }
848 }
849
850 void
851 InitBackEnd3 P((void))
852 {
853     GameMode initialMode;
854     char buf[MSG_SIZ];
855     int err;
856
857     InitChessProgram(&first);
858
859     if (appData.icsActive) {
860         err = establish();
861         if (err != 0) {
862             if (*appData.icsCommPort != NULLCHAR) {
863                 sprintf(buf, "Could not open comm port %s",  
864                         appData.icsCommPort);
865             } else {
866                 sprintf(buf, "Could not connect to host %s, port %s",  
867                         appData.icsHost, appData.icsPort);
868             }
869             DisplayFatalError(buf, err, 1);
870             return;
871         }
872         SetICSMode();
873         telnetISR =
874           AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
875         fromUserISR =
876           AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
877     } else if (appData.noChessProgram) {
878         SetNCPMode();
879     } else {
880         SetGNUMode();
881     }
882
883     if (*appData.cmailGameName != NULLCHAR) {
884         SetCmailMode();
885         OpenLoopback(&cmailPR);
886         cmailISR =
887           AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
888     }
889     
890     ThawUI();
891     DisplayMessage("", "");
892     if (StrCaseCmp(appData.initialMode, "") == 0) {
893       initialMode = BeginningOfGame;
894     } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
895       initialMode = TwoMachinesPlay;
896     } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
897       initialMode = AnalyzeFile; 
898     } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
899       initialMode = AnalyzeMode;
900     } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
901       initialMode = MachinePlaysWhite;
902     } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
903       initialMode = MachinePlaysBlack;
904     } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
905       initialMode = EditGame;
906     } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
907       initialMode = EditPosition;
908     } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
909       initialMode = Training;
910     } else {
911       sprintf(buf, "Unknown initialMode %s", appData.initialMode);
912       DisplayFatalError(buf, 0, 2);
913       return;
914     }
915
916     if (appData.matchMode) {
917         /* Set up machine vs. machine match */
918         if (appData.noChessProgram) {
919             DisplayFatalError("Can't have a match with no chess programs",
920                               0, 2);
921             return;
922         }
923         matchMode = TRUE;
924         matchGame = 1;
925         if (*appData.loadGameFile != NULLCHAR) {
926             if (!LoadGameFromFile(appData.loadGameFile,
927                                   appData.loadGameIndex,
928                                   appData.loadGameFile, FALSE)) {
929                 DisplayFatalError("Bad game file", 0, 1);
930                 return;
931             }
932         } else if (*appData.loadPositionFile != NULLCHAR) {
933             if (!LoadPositionFromFile(appData.loadPositionFile,
934                                       appData.loadPositionIndex,
935                                       appData.loadPositionFile)) {
936                 DisplayFatalError("Bad position file", 0, 1);
937                 return;
938             }
939         }
940         TwoMachinesEvent();
941     } else if (*appData.cmailGameName != NULLCHAR) {
942         /* Set up cmail mode */
943         ReloadCmailMsgEvent(TRUE);
944     } else {
945         /* Set up other modes */
946         if (initialMode == AnalyzeFile) {
947           if (*appData.loadGameFile == NULLCHAR) {
948             DisplayFatalError("AnalyzeFile mode requires a game file", 0, 1);
949             return;
950           }
951         }
952         if (*appData.loadGameFile != NULLCHAR) {
953             (void) LoadGameFromFile(appData.loadGameFile,
954                                     appData.loadGameIndex,
955                                     appData.loadGameFile, TRUE);
956         } else if (*appData.loadPositionFile != NULLCHAR) {
957             (void) LoadPositionFromFile(appData.loadPositionFile,
958                                         appData.loadPositionIndex,
959                                         appData.loadPositionFile);
960         }
961         if (initialMode == AnalyzeMode) {
962           if (appData.noChessProgram) {
963             DisplayFatalError("Analysis mode requires a chess engine", 0, 2);
964             return;
965           }
966           if (appData.icsActive) {
967             DisplayFatalError("Analysis mode does not work with ICS mode",0,2);
968             return;
969           }
970           AnalyzeModeEvent();
971         } else if (initialMode == AnalyzeFile) {
972           ShowThinkingEvent(TRUE);
973           AnalyzeFileEvent();
974           AnalysisPeriodicEvent(1);
975         } else if (initialMode == MachinePlaysWhite) {
976           if (appData.noChessProgram) {
977             DisplayFatalError("MachineWhite mode requires a chess engine",
978                               0, 2);
979             return;
980           }
981           if (appData.icsActive) {
982             DisplayFatalError("MachineWhite mode does not work with ICS mode",
983                               0, 2);
984             return;
985           }
986           MachineWhiteEvent();
987         } else if (initialMode == MachinePlaysBlack) {
988           if (appData.noChessProgram) {
989             DisplayFatalError("MachineBlack mode requires a chess engine",
990                               0, 2);
991             return;
992           }
993           if (appData.icsActive) {
994             DisplayFatalError("MachineBlack mode does not work with ICS mode",
995                               0, 2);
996             return;
997           }
998           MachineBlackEvent();
999         } else if (initialMode == TwoMachinesPlay) {
1000           if (appData.noChessProgram) {
1001             DisplayFatalError("TwoMachines mode requires a chess engine",
1002                               0, 2);
1003             return;
1004           }
1005           if (appData.icsActive) {
1006             DisplayFatalError("TwoMachines mode does not work with ICS mode",
1007                               0, 2);
1008             return;
1009           }
1010           TwoMachinesEvent();
1011         } else if (initialMode == EditGame) {
1012           EditGameEvent();
1013         } else if (initialMode == EditPosition) {
1014           EditPositionEvent();
1015         } else if (initialMode == Training) {
1016           if (*appData.loadGameFile == NULLCHAR) {
1017             DisplayFatalError("Training mode requires a game file", 0, 2);
1018             return;
1019           }
1020           TrainingEvent();
1021         }
1022     }
1023 }
1024
1025 /*
1026  * Establish will establish a contact to a remote host.port.
1027  * Sets icsPR to a ProcRef for a process (or pseudo-process)
1028  *  used to talk to the host.
1029  * Returns 0 if okay, error code if not.
1030  */
1031 int
1032 establish()
1033 {
1034     char buf[MSG_SIZ];
1035
1036     if (*appData.icsCommPort != NULLCHAR) {
1037         /* Talk to the host through a serial comm port */
1038         return OpenCommPort(appData.icsCommPort, &icsPR);
1039
1040     } else if (*appData.gateway != NULLCHAR) {
1041         if (*appData.remoteShell == NULLCHAR) {
1042             /* Use the rcmd protocol to run telnet program on a gateway host */
1043             sprintf(buf, "%s %s %s",
1044                     appData.telnetProgram, appData.icsHost, appData.icsPort);
1045             return OpenRcmd(appData.gateway, appData.remoteUser, buf, &icsPR);
1046
1047         } else {
1048             /* Use the rsh program to run telnet program on a gateway host */
1049             if (*appData.remoteUser == NULLCHAR) {
1050                 sprintf(buf, "%s %s %s %s %s", appData.remoteShell,
1051                         appData.gateway, appData.telnetProgram,
1052                         appData.icsHost, appData.icsPort);
1053             } else {
1054                 sprintf(buf, "%s %s -l %s %s %s %s",
1055                         appData.remoteShell, appData.gateway, 
1056                         appData.remoteUser, appData.telnetProgram,
1057                         appData.icsHost, appData.icsPort);
1058             }
1059             return StartChildProcess(buf, "", &icsPR);
1060
1061         }
1062     } else if (appData.useTelnet) {
1063         return OpenTelnet(appData.icsHost, appData.icsPort, &icsPR);
1064
1065     } else {
1066         /* TCP socket interface differs somewhat between
1067            Unix and NT; handle details in the front end.
1068            */
1069         return OpenTCP(appData.icsHost, appData.icsPort, &icsPR);
1070     }
1071 }
1072
1073 void
1074 show_bytes(fp, buf, count)
1075      FILE *fp;
1076      char *buf;
1077      int count;
1078 {
1079     while (count--) {
1080         if (*buf < 040 || *(unsigned char *) buf > 0177) {
1081             fprintf(fp, "\\%03o", *buf & 0xff);
1082         } else {
1083             putc(*buf, fp);
1084         }
1085         buf++;
1086     }
1087     fflush(fp);
1088 }
1089
1090 /* Returns an errno value */
1091 int
1092 OutputMaybeTelnet(pr, message, count, outError)
1093      ProcRef pr;
1094      char *message;
1095      int count;
1096      int *outError;
1097 {
1098     char buf[8192], *p, *q, *buflim;
1099     int left, newcount, outcount;
1100
1101     if (*appData.icsCommPort != NULLCHAR || appData.useTelnet ||
1102         *appData.gateway != NULLCHAR) {
1103         if (appData.debugMode) {
1104             fprintf(debugFP, ">ICS: ");
1105             show_bytes(debugFP, message, count);
1106             fprintf(debugFP, "\n");
1107         }
1108         return OutputToProcess(pr, message, count, outError);
1109     }
1110
1111     buflim = &buf[sizeof(buf)-1]; /* allow 1 byte for expanding last char */
1112     p = message;
1113     q = buf;
1114     left = count;
1115     newcount = 0;
1116     while (left) {
1117         if (q >= buflim) {
1118             if (appData.debugMode) {
1119                 fprintf(debugFP, ">ICS: ");
1120                 show_bytes(debugFP, buf, newcount);
1121                 fprintf(debugFP, "\n");
1122             }
1123             outcount = OutputToProcess(pr, buf, newcount, outError);
1124             if (outcount < newcount) return -1; /* to be sure */
1125             q = buf;
1126             newcount = 0;
1127         }
1128         if (*p == '\n') {
1129             *q++ = '\r';
1130             newcount++;
1131         } else if (((unsigned char) *p) == TN_IAC) {
1132             *q++ = (char) TN_IAC;
1133             newcount ++;
1134         }
1135         *q++ = *p++;
1136         newcount++;
1137         left--;
1138     }
1139     if (appData.debugMode) {
1140         fprintf(debugFP, ">ICS: ");
1141         show_bytes(debugFP, buf, newcount);
1142         fprintf(debugFP, "\n");
1143     }
1144     outcount = OutputToProcess(pr, buf, newcount, outError);
1145     if (outcount < newcount) return -1; /* to be sure */
1146     return count;
1147 }
1148
1149 void
1150 read_from_player(isr, closure, message, count, error)
1151      InputSourceRef isr;
1152      VOIDSTAR closure;
1153      char *message;
1154      int count;
1155      int error;
1156 {
1157     int outError, outCount;
1158     static int gotEof = 0;
1159
1160     /* Pass data read from player on to ICS */
1161     if (count > 0) {
1162         gotEof = 0;
1163         outCount = OutputMaybeTelnet(icsPR, message, count, &outError);
1164         if (outCount < count) {
1165             DisplayFatalError("Error writing to ICS", outError, 1);
1166         }
1167     } else if (count < 0) {
1168         RemoveInputSource(isr);
1169         DisplayFatalError("Error reading from keyboard", error, 1);
1170     } else if (gotEof++ > 0) {
1171         RemoveInputSource(isr);
1172         DisplayFatalError("Got end of file from keyboard", 0, 0);
1173     }
1174 }
1175
1176 void
1177 SendToICS(s)
1178      char *s;
1179 {
1180     int count, outCount, outError;
1181
1182     if (icsPR == NULL) return;
1183
1184     count = strlen(s);
1185     outCount = OutputMaybeTelnet(icsPR, s, count, &outError);
1186     if (outCount < count) {
1187         DisplayFatalError("Error writing to ICS", outError, 1);
1188     }
1189 }
1190
1191 /* This is used for sending logon scripts to the ICS. Sending
1192    without a delay causes problems when using timestamp on ICC
1193    (at least on my machine). */
1194 void
1195 SendToICSDelayed(s,msdelay)
1196      char *s;
1197      long msdelay;
1198 {
1199     int count, outCount, outError;
1200
1201     if (icsPR == NULL) return;
1202
1203     count = strlen(s);
1204     if (appData.debugMode) {
1205         fprintf(debugFP, ">ICS: ");
1206         show_bytes(debugFP, s, count);
1207         fprintf(debugFP, "\n");
1208     }
1209     outCount = OutputToProcessDelayed(icsPR, s, count, &outError,
1210                                       msdelay);
1211     if (outCount < count) {
1212         DisplayFatalError("Error writing to ICS", outError, 1);
1213     }
1214 }
1215
1216
1217 /* Remove all highlighting escape sequences in s
1218    Also deletes any suffix starting with '(' 
1219    */
1220 char *
1221 StripHighlightAndTitle(s)
1222      char *s;
1223 {
1224     static char retbuf[MSG_SIZ];
1225     char *p = retbuf;
1226
1227     while (*s != NULLCHAR) {
1228         while (*s == '\033') {
1229             while (*s != NULLCHAR && !isalpha(*s)) s++;
1230             if (*s != NULLCHAR) s++;
1231         }
1232         while (*s != NULLCHAR && *s != '\033') {
1233             if (*s == '(' || *s == '[') {
1234                 *p = NULLCHAR;
1235                 return retbuf;
1236             }
1237             *p++ = *s++;
1238         }
1239     }
1240     *p = NULLCHAR;
1241     return retbuf;
1242 }
1243
1244 /* Remove all highlighting escape sequences in s */
1245 char *
1246 StripHighlight(s)
1247      char *s;
1248 {
1249     static char retbuf[MSG_SIZ];
1250     char *p = retbuf;
1251
1252     while (*s != NULLCHAR) {
1253         while (*s == '\033') {
1254             while (*s != NULLCHAR && !isalpha(*s)) s++;
1255             if (*s != NULLCHAR) s++;
1256         }
1257         while (*s != NULLCHAR && *s != '\033') {
1258             *p++ = *s++;
1259         }
1260     }
1261     *p = NULLCHAR;
1262     return retbuf;
1263 }
1264
1265 char *variantNames[] = VARIANT_NAMES;
1266 char *
1267 VariantName(v)
1268      VariantClass v;
1269 {
1270     return variantNames[v];
1271 }
1272
1273
1274 /* Identify a variant from the strings the chess servers use or the
1275    PGN Variant tag names we use. */
1276 VariantClass
1277 StringToVariant(e)
1278      char *e;
1279 {
1280     char *p;
1281     int wnum = -1;
1282     VariantClass v = VariantNormal;
1283     int i, found = FALSE;
1284     char buf[MSG_SIZ];
1285
1286     if (!e) return v;
1287     
1288     for (i=0; i<sizeof(variantNames)/sizeof(char*); i++) {
1289       if (StrCaseStr(e, variantNames[i])) {
1290         v = (VariantClass) i;
1291         found = TRUE;
1292         break;
1293       }
1294     }
1295
1296     if (!found) {
1297       if ((StrCaseStr(e, "fischer") && StrCaseStr(e, "random"))
1298           || StrCaseStr(e, "wild/fr")) {
1299         v = VariantFischeRandom;
1300       } else if ((i = 4, p = StrCaseStr(e, "wild")) ||
1301                  (i = 1, p = StrCaseStr(e, "w"))) {
1302         p += i;
1303         while (*p && (isspace(*p) || *p == '(' || *p == '/')) p++;
1304         if (isdigit(*p)) {
1305           wnum = atoi(p);
1306         } else {
1307           wnum = -1;
1308         }
1309         switch (wnum) {
1310         case 0: /* FICS only, actually */
1311         case 1:
1312           /* Castling legal even if K starts on d-file */
1313           v = VariantWildCastle;
1314           break;
1315         case 2:
1316         case 3:
1317         case 4:
1318           /* Castling illegal even if K & R happen to start in
1319              normal positions. */
1320           v = VariantNoCastle;
1321           break;
1322         case 5:
1323         case 7:
1324         case 8:
1325         case 10:
1326         case 11:
1327         case 12:
1328         case 13:
1329         case 14:
1330         case 15:
1331         case 18:
1332         case 19:
1333           /* Castling legal iff K & R start in normal positions */
1334           v = VariantNormal;
1335           break;
1336         case 6:
1337         case 20:
1338         case 21:
1339           /* Special wilds for position setup; unclear what to do here */
1340           v = VariantLoadable;
1341           break;
1342         case 9:
1343           /* Bizarre ICC game */
1344           v = VariantTwoKings;
1345           break;
1346         case 16:
1347           v = VariantKriegspiel;
1348           break;
1349         case 17:
1350           v = VariantLosers;
1351           break;
1352         case 22:
1353           v = VariantFischeRandom;
1354           break;
1355         case 23:
1356           v = VariantCrazyhouse;
1357           break;
1358         case 24:
1359           v = VariantBughouse;
1360           break;
1361         case 25:
1362           v = Variant3Check;
1363           break;
1364         case 26:
1365           /* Not quite the same as FICS suicide! */
1366           v = VariantGiveaway;
1367           break;
1368         case 27:
1369           v = VariantAtomic;
1370           break;
1371         case 28:
1372           v = VariantShatranj;
1373           break;
1374
1375         /* Temporary names for future ICC types.  The name *will* change in 
1376            the next xboard/WinBoard release after ICC defines it. */
1377         case 29:
1378           v = Variant29;
1379           break;
1380         case 30:
1381           v = Variant30;
1382           break;
1383         case 31:
1384           v = Variant31;
1385           break;
1386         case 32:
1387           v = Variant32;
1388           break;
1389         case 33:
1390           v = Variant33;
1391           break;
1392         case 34:
1393           v = Variant34;
1394           break;
1395         case 35:
1396           v = Variant35;
1397           break;
1398         case 36:
1399           v = Variant36;
1400           break;
1401
1402         case -1:
1403           /* Found "wild" or "w" in the string but no number;
1404              must assume it's normal chess. */
1405           v = VariantNormal;
1406           break;
1407         default:
1408           sprintf(buf, "Unknown wild type %d", wnum);
1409           DisplayError(buf, 0);
1410           v = VariantUnknown;
1411           break;
1412         }
1413       }
1414     }
1415     if (appData.debugMode) {
1416       fprintf(debugFP, "recognized '%s' (%d) as variant %s\n",
1417               e, wnum, VariantName(v));
1418     }
1419     return v;
1420 }
1421
1422 static int leftover_start = 0, leftover_len = 0;
1423 char star_match[STAR_MATCH_N][MSG_SIZ];
1424
1425 /* Test whether pattern is present at &buf[*index]; if so, return TRUE,
1426    advance *index beyond it, and set leftover_start to the new value of
1427    *index; else return FALSE.  If pattern contains the character '*', it
1428    matches any sequence of characters not containing '\r', '\n', or the
1429    character following the '*' (if any), and the matched sequence(s) are
1430    copied into star_match.
1431    */
1432 int
1433 looking_at(buf, index, pattern)
1434      char *buf;
1435      int *index;
1436      char *pattern;
1437 {
1438     char *bufp = &buf[*index], *patternp = pattern;
1439     int star_count = 0;
1440     char *matchp = star_match[0];
1441     
1442     for (;;) {
1443         if (*patternp == NULLCHAR) {
1444             *index = leftover_start = bufp - buf;
1445             *matchp = NULLCHAR;
1446             return TRUE;
1447         }
1448         if (*bufp == NULLCHAR) return FALSE;
1449         if (*patternp == '*') {
1450             if (*bufp == *(patternp + 1)) {
1451                 *matchp = NULLCHAR;
1452                 matchp = star_match[++star_count];
1453                 patternp += 2;
1454                 bufp++;
1455                 continue;
1456             } else if (*bufp == '\n' || *bufp == '\r') {
1457                 patternp++;
1458                 if (*patternp == NULLCHAR)
1459                   continue;
1460                 else
1461                   return FALSE;
1462             } else {
1463                 *matchp++ = *bufp++;
1464                 continue;
1465             }
1466         }
1467         if (*patternp != *bufp) return FALSE;
1468         patternp++;
1469         bufp++;
1470     }
1471 }
1472
1473 void
1474 SendToPlayer(data, length)
1475      char *data;
1476      int length;
1477 {
1478     int error, outCount;
1479     outCount = OutputToProcess(NoProc, data, length, &error);
1480     if (outCount < length) {
1481         DisplayFatalError("Error writing to display", error, 1);
1482     }
1483 }
1484
1485 void
1486 PackHolding(packed, holding)
1487      char packed[];
1488      char *holding;
1489 {
1490     char *p = holding;
1491     char *q = packed;
1492     int runlength = 0;
1493     int curr = 9999;
1494     do {
1495         if (*p == curr) {
1496             runlength++;
1497         } else {
1498             switch (runlength) {
1499               case 0:
1500                 break;
1501               case 1:
1502                 *q++ = curr;
1503                 break;
1504               case 2:
1505                 *q++ = curr;
1506                 *q++ = curr;
1507                 break;
1508               default:
1509                 sprintf(q, "%d", runlength);
1510                 while (*q) q++;
1511                 *q++ = curr;
1512                 break;
1513             }
1514             runlength = 1;
1515             curr = *p;
1516         }
1517     } while (*p++);
1518     *q = NULLCHAR;
1519 }
1520
1521 /* Telnet protocol requests from the front end */
1522 void
1523 TelnetRequest(ddww, option)
1524      unsigned char ddww, option;
1525 {
1526     unsigned char msg[3];
1527     int outCount, outError;
1528
1529     if (*appData.icsCommPort != NULLCHAR || appData.useTelnet) return;
1530
1531     if (appData.debugMode) {
1532         char buf1[8], buf2[8], *ddwwStr, *optionStr;
1533         switch (ddww) {
1534           case TN_DO:
1535             ddwwStr = "DO";
1536             break;
1537           case TN_DONT:
1538             ddwwStr = "DONT";
1539             break;
1540           case TN_WILL:
1541             ddwwStr = "WILL";
1542             break;
1543           case TN_WONT:
1544             ddwwStr = "WONT";
1545             break;
1546           default:
1547             ddwwStr = buf1;
1548             sprintf(buf1, "%d", ddww);
1549             break;
1550         }
1551         switch (option) {
1552           case TN_ECHO:
1553             optionStr = "ECHO";
1554             break;
1555           default:
1556             optionStr = buf2;
1557             sprintf(buf2, "%d", option);
1558             break;
1559         }
1560         fprintf(debugFP, ">%s %s ", ddwwStr, optionStr);
1561     }
1562     msg[0] = TN_IAC;
1563     msg[1] = ddww;
1564     msg[2] = option;
1565     outCount = OutputToProcess(icsPR, (char *)msg, 3, &outError);
1566     if (outCount < 3) {
1567         DisplayFatalError("Error writing to ICS", outError, 1);
1568     }
1569 }
1570
1571 void
1572 DoEcho()
1573 {
1574     if (!appData.icsActive) return;
1575     TelnetRequest(TN_DO, TN_ECHO);
1576 }
1577
1578 void
1579 DontEcho()
1580 {
1581     if (!appData.icsActive) return;
1582     TelnetRequest(TN_DONT, TN_ECHO);
1583 }
1584
1585 static int loggedOn = FALSE;
1586
1587 /*-- Game start info cache: --*/
1588 int gs_gamenum;
1589 char gs_kind[MSG_SIZ];
1590 static char player1Name[128] = "";
1591 static char player2Name[128] = "";
1592 static int player1Rating = -1;
1593 static int player2Rating = -1;
1594 /*----------------------------*/
1595
1596 void
1597 read_from_ics(isr, closure, data, count, error)
1598      InputSourceRef isr;
1599      VOIDSTAR closure;
1600      char *data;
1601      int count;
1602      int error;
1603 {
1604 #define BUF_SIZE 8192
1605 #define STARTED_NONE 0
1606 #define STARTED_MOVES 1
1607 #define STARTED_BOARD 2
1608 #define STARTED_OBSERVE 3
1609 #define STARTED_HOLDINGS 4
1610 #define STARTED_CHATTER 5
1611 #define STARTED_COMMENT 6
1612 #define STARTED_MOVES_NOHIDE 7
1613     
1614     static int started = STARTED_NONE;
1615     static char parse[20000];
1616     static int parse_pos = 0;
1617     static char buf[BUF_SIZE + 1];
1618     static int firstTime = TRUE, intfSet = FALSE;
1619     static ColorClass curColor = ColorNormal;
1620     static ColorClass prevColor = ColorNormal;
1621     static int savingComment = FALSE;
1622     char str[500];
1623     int i, oldi;
1624     int buf_len;
1625     int next_out;
1626     int tkind;
1627     char *p;
1628
1629 #ifdef WIN32
1630     if (appData.debugMode) {
1631       if (!error) {
1632         fprintf(debugFP, "<ICS: ");
1633         show_bytes(debugFP, data, count);
1634         fprintf(debugFP, "\n");
1635       }
1636     }
1637 #endif
1638
1639     if (count > 0) {
1640         /* If last read ended with a partial line that we couldn't parse,
1641            prepend it to the new read and try again. */
1642         if (leftover_len > 0) {
1643             for (i=0; i<leftover_len; i++)
1644               buf[i] = buf[leftover_start + i];
1645         }
1646
1647         /* Copy in new characters, removing nulls and \r's */
1648         buf_len = leftover_len;
1649         for (i = 0; i < count; i++) {
1650             if (data[i] != NULLCHAR && data[i] != '\r')
1651               buf[buf_len++] = data[i];
1652         }
1653
1654         buf[buf_len] = NULLCHAR;
1655         next_out = leftover_len;
1656         leftover_start = 0;
1657         
1658         i = 0;
1659         while (i < buf_len) {
1660             /* Deal with part of the TELNET option negotiation
1661                protocol.  We refuse to do anything beyond the
1662                defaults, except that we allow the WILL ECHO option,
1663                which ICS uses to turn off password echoing when we are
1664                directly connected to it.  We reject this option
1665                if localLineEditing mode is on (always on in xboard)
1666                and we are talking to port 23, which might be a real
1667                telnet server that will try to keep WILL ECHO on permanently.
1668              */
1669             if (buf_len - i >= 3 && (unsigned char) buf[i] == TN_IAC) {
1670                 static int remoteEchoOption = FALSE; /* telnet ECHO option */
1671                 unsigned char option;
1672                 oldi = i;
1673                 switch ((unsigned char) buf[++i]) {
1674                   case TN_WILL:
1675                     if (appData.debugMode)
1676                       fprintf(debugFP, "\n<WILL ");
1677                     switch (option = (unsigned char) buf[++i]) {
1678                       case TN_ECHO:
1679                         if (appData.debugMode)
1680                           fprintf(debugFP, "ECHO ");
1681                         /* Reply only if this is a change, according
1682                            to the protocol rules. */
1683                         if (remoteEchoOption) break;
1684                         if (appData.localLineEditing &&
1685                             atoi(appData.icsPort) == TN_PORT) {
1686                             TelnetRequest(TN_DONT, TN_ECHO);
1687                         } else {
1688                             EchoOff();
1689                             TelnetRequest(TN_DO, TN_ECHO);
1690                             remoteEchoOption = TRUE;
1691                         }
1692                         break;
1693                       default:
1694                         if (appData.debugMode)
1695                           fprintf(debugFP, "%d ", option);
1696                         /* Whatever this is, we don't want it. */
1697                         TelnetRequest(TN_DONT, option);
1698                         break;
1699                     }
1700                     break;
1701                   case TN_WONT:
1702                     if (appData.debugMode)
1703                       fprintf(debugFP, "\n<WONT ");
1704                     switch (option = (unsigned char) buf[++i]) {
1705                       case TN_ECHO:
1706                         if (appData.debugMode)
1707                           fprintf(debugFP, "ECHO ");
1708                         /* Reply only if this is a change, according
1709                            to the protocol rules. */
1710                         if (!remoteEchoOption) break;
1711                         EchoOn();
1712                         TelnetRequest(TN_DONT, TN_ECHO);
1713                         remoteEchoOption = FALSE;
1714                         break;
1715                       default:
1716                         if (appData.debugMode)
1717                           fprintf(debugFP, "%d ", (unsigned char) option);
1718                         /* Whatever this is, it must already be turned
1719                            off, because we never agree to turn on
1720                            anything non-default, so according to the
1721                            protocol rules, we don't reply. */
1722                         break;
1723                     }
1724                     break;
1725                   case TN_DO:
1726                     if (appData.debugMode)
1727                       fprintf(debugFP, "\n<DO ");
1728                     switch (option = (unsigned char) buf[++i]) {
1729                       default:
1730                         /* Whatever this is, we refuse to do it. */
1731                         if (appData.debugMode)
1732                           fprintf(debugFP, "%d ", option);
1733                         TelnetRequest(TN_WONT, option);
1734                         break;
1735                     }
1736                     break;
1737                   case TN_DONT:
1738                     if (appData.debugMode)
1739                       fprintf(debugFP, "\n<DONT ");
1740                     switch (option = (unsigned char) buf[++i]) {
1741                       default:
1742                         if (appData.debugMode)
1743                           fprintf(debugFP, "%d ", option);
1744                         /* Whatever this is, we are already not doing
1745                            it, because we never agree to do anything
1746                            non-default, so according to the protocol
1747                            rules, we don't reply. */
1748                         break;
1749                     }
1750                     break;
1751                   case TN_IAC:
1752                     if (appData.debugMode)
1753                       fprintf(debugFP, "\n<IAC ");
1754                     /* Doubled IAC; pass it through */
1755                     i--;
1756                     break;
1757                   default:
1758                     if (appData.debugMode)
1759                       fprintf(debugFP, "\n<%d ", (unsigned char) buf[i]);
1760                     /* Drop all other telnet commands on the floor */
1761                     break;
1762                 }
1763                 if (oldi > next_out)
1764                   SendToPlayer(&buf[next_out], oldi - next_out);
1765                 if (++i > next_out)
1766                   next_out = i;
1767                 continue;
1768             }
1769                 
1770             /* OK, this at least will *usually* work */
1771             if (!loggedOn && looking_at(buf, &i, "ics%")) {
1772                 loggedOn = TRUE;
1773             }
1774             
1775             if (loggedOn && !intfSet) {
1776                 if (ics_type == ICS_ICC) {
1777                   sprintf(str,
1778                           "/set-quietly interface %s\n/set-quietly style 12\n",
1779                           programVersion);
1780
1781                 } else if (ics_type == ICS_CHESSNET) {
1782                   sprintf(str, "/style 12\n");
1783                 } else {
1784                   strcpy(str, "alias $ @\n$set interface ");
1785                   strcat(str, programVersion);
1786                   strcat(str, "\n$iset startpos 1\n$iset ms 1\n");
1787 #ifdef WIN32
1788                   strcat(str, "$iset nohighlight 1\n");
1789 #endif
1790                   strcat(str, "$iset lock 1\n$style 12\n");
1791                 }
1792                 SendToICS(str);
1793                 intfSet = TRUE;
1794             }
1795
1796             if (started == STARTED_COMMENT) {
1797                 /* Accumulate characters in comment */
1798                 parse[parse_pos++] = buf[i];
1799                 if (buf[i] == '\n') {
1800                     parse[parse_pos] = NULLCHAR;
1801                     AppendComment(forwardMostMove, StripHighlight(parse));
1802                     started = STARTED_NONE;
1803                 } else {
1804                     /* Don't match patterns against characters in chatter */
1805                     i++;
1806                     continue;
1807                 }
1808             }
1809             if (started == STARTED_CHATTER) {
1810                 if (buf[i] != '\n') {
1811                     /* Don't match patterns against characters in chatter */
1812                     i++;
1813                     continue;
1814                 }
1815                 started = STARTED_NONE;
1816             }
1817
1818             /* Kludge to deal with rcmd protocol */
1819             if (firstTime && looking_at(buf, &i, "\001*")) {
1820                 DisplayFatalError(&buf[1], 0, 1);
1821                 continue;
1822             } else {
1823                 firstTime = FALSE;
1824             }
1825
1826             if (!loggedOn && looking_at(buf, &i, "chessclub.com")) {
1827                 ics_type = ICS_ICC;
1828                 ics_prefix = "/";
1829                 if (appData.debugMode)
1830                   fprintf(debugFP, "ics_type %d\n", ics_type);
1831                 continue;
1832             }
1833             if (!loggedOn && looking_at(buf, &i, "freechess.org")) {
1834                 ics_type = ICS_FICS;
1835                 ics_prefix = "$";
1836                 if (appData.debugMode)
1837                   fprintf(debugFP, "ics_type %d\n", ics_type);
1838                 continue;
1839             }
1840             if (!loggedOn && looking_at(buf, &i, "chess.net")) {
1841                 ics_type = ICS_CHESSNET;
1842                 ics_prefix = "/";
1843                 if (appData.debugMode)
1844                   fprintf(debugFP, "ics_type %d\n", ics_type);
1845                 continue;
1846             }
1847
1848             if (!loggedOn &&
1849                 (looking_at(buf, &i, "\"*\" is *a registered name") ||
1850                  looking_at(buf, &i, "Logging you in as \"*\"") ||
1851                  looking_at(buf, &i, "will be \"*\""))) {
1852               strcpy(ics_handle, star_match[0]);
1853               continue;
1854             }
1855
1856             if (loggedOn && !have_set_title && ics_handle[0] != NULLCHAR) {
1857               char buf[MSG_SIZ];
1858               sprintf(buf, "%s@%s", ics_handle, appData.icsHost);
1859               DisplayIcsInteractionTitle(buf);
1860               have_set_title = TRUE;
1861             }
1862
1863             /* skip finger notes */
1864             if (started == STARTED_NONE &&
1865                 ((buf[i] == ' ' && isdigit(buf[i+1])) ||
1866                  (buf[i] == '1' && buf[i+1] == '0')) &&
1867                 buf[i+2] == ':' && buf[i+3] == ' ') {
1868               started = STARTED_CHATTER;
1869               i += 3;
1870               continue;
1871             }
1872
1873             /* skip formula vars */
1874             if (started == STARTED_NONE &&
1875                 buf[i] == 'f' && isdigit(buf[i+1]) && buf[i+2] == ':') {
1876               started = STARTED_CHATTER;
1877               i += 3;
1878               continue;
1879             }
1880
1881             oldi = i;
1882             if (appData.zippyTalk || appData.zippyPlay) {
1883 #if ZIPPY
1884                 if (ZippyControl(buf, &i) ||
1885                     ZippyConverse(buf, &i) ||
1886                     (appData.zippyPlay && ZippyMatch(buf, &i))) {
1887                     loggedOn = TRUE;
1888                     continue;
1889                 }
1890 #endif
1891             } else {
1892                 if (/* Don't color "message" or "messages" output */
1893                     (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
1894                     looking_at(buf, &i, "*. * at *:*: ") ||
1895                     looking_at(buf, &i, "--* (*:*): ") ||
1896                     /* Regular tells and says */
1897                     (tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
1898                     looking_at(buf, &i, "* (your partner) tells you: ") ||
1899                     looking_at(buf, &i, "* says: ") ||
1900                     /* Message notifications (same color as tells) */
1901                     looking_at(buf, &i, "* has left a message ") ||
1902                     looking_at(buf, &i, "* just sent you a message:\n") ||
1903                     /* Whispers and kibitzes */
1904                     (tkind = 2, looking_at(buf, &i, "* whispers: ")) ||
1905                     looking_at(buf, &i, "* kibitzes: ") ||
1906                     /* Channel tells */
1907                     (tkind = 3, looking_at(buf, &i, "*(*: "))) {
1908
1909                   if (tkind == 1 && strchr(star_match[0], ':')) {
1910                       /* Avoid "tells you:" spoofs in channels */
1911                      tkind = 3;
1912                   }
1913                   if (star_match[0][0] == NULLCHAR ||
1914                       strchr(star_match[0], ' ') ||
1915                       (tkind == 3 && strchr(star_match[1], ' '))) {
1916                     /* Reject bogus matches */
1917                     i = oldi;
1918                   } else {
1919                     if (appData.colorize) {
1920                       if (oldi > next_out) {
1921                         SendToPlayer(&buf[next_out], oldi - next_out);
1922                         next_out = oldi;
1923                       }
1924                       switch (tkind) {
1925                       case 1:
1926                         Colorize(ColorTell, FALSE);
1927                         curColor = ColorTell;
1928                         break;
1929                       case 2:
1930                         Colorize(ColorKibitz, FALSE);
1931                         curColor = ColorKibitz;
1932                         break;
1933                       case 3:
1934                         p = strrchr(star_match[1], '(');
1935                         if (p == NULL) {
1936                           p = star_match[1];
1937                         } else {
1938                           p++;
1939                         }
1940                         if (atoi(p) == 1) {
1941                           Colorize(ColorChannel1, FALSE);
1942                           curColor = ColorChannel1;
1943                         } else {
1944                           Colorize(ColorChannel, FALSE);
1945                           curColor = ColorChannel;
1946                         }
1947                         break;
1948                       case 5:
1949                         curColor = ColorNormal;
1950                         break;
1951                       }
1952                     }
1953                     if (started == STARTED_NONE && appData.autoComment &&
1954                         (gameMode == IcsObserving ||
1955                          gameMode == IcsPlayingWhite ||
1956                          gameMode == IcsPlayingBlack)) {
1957                       parse_pos = i - oldi;
1958                       memcpy(parse, &buf[oldi], parse_pos);
1959                       parse[parse_pos] = NULLCHAR;
1960                       started = STARTED_COMMENT;
1961                       savingComment = TRUE;
1962                     } else {
1963                       started = STARTED_CHATTER;
1964                       savingComment = FALSE;
1965                     }
1966                     loggedOn = TRUE;
1967                     continue;
1968                   }
1969                 }
1970
1971                 if (looking_at(buf, &i, "* s-shouts: ") ||
1972                     looking_at(buf, &i, "* c-shouts: ")) {
1973                     if (appData.colorize) {
1974                         if (oldi > next_out) {
1975                             SendToPlayer(&buf[next_out], oldi - next_out);
1976                             next_out = oldi;
1977                         }
1978                         Colorize(ColorSShout, FALSE);
1979                         curColor = ColorSShout;
1980                     }
1981                     loggedOn = TRUE;
1982                     started = STARTED_CHATTER;
1983                     continue;
1984                 }
1985
1986                 if (looking_at(buf, &i, "--->")) {
1987                     loggedOn = TRUE;
1988                     continue;
1989                 }
1990
1991                 if (looking_at(buf, &i, "* shouts: ") ||
1992                     looking_at(buf, &i, "--> ")) {
1993                     if (appData.colorize) {
1994                         if (oldi > next_out) {
1995                             SendToPlayer(&buf[next_out], oldi - next_out);
1996                             next_out = oldi;
1997                         }
1998                         Colorize(ColorShout, FALSE);
1999                         curColor = ColorShout;
2000                     }
2001                     loggedOn = TRUE;
2002                     started = STARTED_CHATTER;
2003                     continue;
2004                 }
2005
2006                 if (looking_at( buf, &i, "Challenge:")) {
2007                     if (appData.colorize) {
2008                         if (oldi > next_out) {
2009                             SendToPlayer(&buf[next_out], oldi - next_out);
2010                             next_out = oldi;
2011                         }
2012                         Colorize(ColorChallenge, FALSE);
2013                         curColor = ColorChallenge;
2014                     }
2015                     loggedOn = TRUE;
2016                     continue;
2017                 }
2018
2019                 if (looking_at(buf, &i, "* offers you") ||
2020                     looking_at(buf, &i, "* offers to be") ||
2021                     looking_at(buf, &i, "* would like to") ||
2022                     looking_at(buf, &i, "* requests to") ||
2023                     looking_at(buf, &i, "Your opponent offers") ||
2024                     looking_at(buf, &i, "Your opponent requests")) {
2025
2026                     if (appData.colorize) {
2027                         if (oldi > next_out) {
2028                             SendToPlayer(&buf[next_out], oldi - next_out);
2029                             next_out = oldi;
2030                         }
2031                         Colorize(ColorRequest, FALSE);
2032                         curColor = ColorRequest;
2033                     }
2034                     continue;
2035                 }
2036
2037                 if (looking_at(buf, &i, "* (*) seeking")) {
2038                     if (appData.colorize) {
2039                         if (oldi > next_out) {
2040                             SendToPlayer(&buf[next_out], oldi - next_out);
2041                             next_out = oldi;
2042                         }
2043                         Colorize(ColorSeek, FALSE);
2044                         curColor = ColorSeek;
2045                     }
2046                     continue;
2047                 }
2048             }
2049
2050             if (looking_at(buf, &i, "\\   ")) {
2051                 if (prevColor != ColorNormal) {
2052                     if (oldi > next_out) {
2053                         SendToPlayer(&buf[next_out], oldi - next_out);
2054                         next_out = oldi;
2055                     }
2056                     Colorize(prevColor, TRUE);
2057                     curColor = prevColor;
2058                 }
2059                 if (savingComment) {
2060                     parse_pos = i - oldi;
2061                     memcpy(parse, &buf[oldi], parse_pos);
2062                     parse[parse_pos] = NULLCHAR;
2063                     started = STARTED_COMMENT;
2064                 } else {
2065                     started = STARTED_CHATTER;
2066                 }
2067                 continue;
2068             }
2069
2070             if (looking_at(buf, &i, "Black Strength :") ||
2071                 looking_at(buf, &i, "<<< style 10 board >>>") ||
2072                 looking_at(buf, &i, "<10>") ||
2073                 looking_at(buf, &i, "#@#")) {
2074                 /* Wrong board style */
2075                 loggedOn = TRUE;
2076                 SendToICS(ics_prefix);
2077                 SendToICS("set style 12\n");
2078                 SendToICS(ics_prefix);
2079                 SendToICS("refresh\n");
2080                 continue;
2081             }
2082             
2083             if (!have_sent_ICS_logon && looking_at(buf, &i, "login:")) {
2084                 ICSInitScript();
2085                 have_sent_ICS_logon = 1;
2086                 continue;
2087             }
2088               
2089             if (ics_getting_history != H_GETTING_MOVES /*smpos kludge*/ && 
2090                 (looking_at(buf, &i, "\n<12> ") ||
2091                  looking_at(buf, &i, "<12> "))) {
2092                 loggedOn = TRUE;
2093                 if (oldi > next_out) {
2094                     SendToPlayer(&buf[next_out], oldi - next_out);
2095                 }
2096                 next_out = i;
2097                 started = STARTED_BOARD;
2098                 parse_pos = 0;
2099                 continue;
2100             }
2101
2102             if ((started == STARTED_NONE && looking_at(buf, &i, "\n<b1> ")) ||
2103                 looking_at(buf, &i, "<b1> ")) {
2104                 if (oldi > next_out) {
2105                     SendToPlayer(&buf[next_out], oldi - next_out);
2106                 }
2107                 next_out = i;
2108                 started = STARTED_HOLDINGS;
2109                 parse_pos = 0;
2110                 continue;
2111             }
2112
2113             if (looking_at(buf, &i, "* *vs. * *--- *")) {
2114                 loggedOn = TRUE;
2115                 /* Header for a move list -- first line */
2116
2117                 switch (ics_getting_history) {
2118                   case H_FALSE:
2119                     switch (gameMode) {
2120                       case IcsIdle:
2121                       case BeginningOfGame:
2122                         /* User typed "moves" or "oldmoves" while we
2123                            were idle.  Pretend we asked for these
2124                            moves and soak them up so user can step
2125                            through them and/or save them.
2126                            */
2127                         Reset(FALSE, TRUE);
2128                         gameMode = IcsObserving;
2129                         ModeHighlight();
2130                         ics_gamenum = -1;
2131                         ics_getting_history = H_GOT_UNREQ_HEADER;
2132                         break;
2133                       case EditGame: /*?*/
2134                       case EditPosition: /*?*/
2135                         /* Should above feature work in these modes too? */
2136                         /* For now it doesn't */
2137                         ics_getting_history = H_GOT_UNWANTED_HEADER;
2138                         break;
2139                       default:
2140                         ics_getting_history = H_GOT_UNWANTED_HEADER;
2141                         break;
2142                     }
2143                     break;
2144                   case H_REQUESTED:
2145                     /* Is this the right one? */
2146                     if (gameInfo.white && gameInfo.black &&
2147                         strcmp(gameInfo.white, star_match[0]) == 0 &&
2148                         strcmp(gameInfo.black, star_match[2]) == 0) {
2149                         /* All is well */
2150                         ics_getting_history = H_GOT_REQ_HEADER;
2151                     }
2152                     break;
2153                   case H_GOT_REQ_HEADER:
2154                   case H_GOT_UNREQ_HEADER:
2155                   case H_GOT_UNWANTED_HEADER:
2156                   case H_GETTING_MOVES:
2157                     /* Should not happen */
2158                     DisplayError("Error gathering move list: two headers", 0);
2159                     ics_getting_history = H_FALSE;
2160                     break;
2161                 }
2162
2163                 /* Save player ratings into gameInfo if needed */
2164                 if ((ics_getting_history == H_GOT_REQ_HEADER ||
2165                      ics_getting_history == H_GOT_UNREQ_HEADER) &&
2166                     (gameInfo.whiteRating == -1 ||
2167                      gameInfo.blackRating == -1)) {
2168
2169                     gameInfo.whiteRating = string_to_rating(star_match[1]);
2170                     gameInfo.blackRating = string_to_rating(star_match[3]);
2171                     if (appData.debugMode)
2172                       fprintf(debugFP, "Ratings from header: W %d, B %d\n", 
2173                               gameInfo.whiteRating, gameInfo.blackRating);
2174                 }
2175                 continue;
2176             }
2177
2178             if (looking_at(buf, &i,
2179               "* * match, initial time: * minute*, increment: * second")) {
2180                 /* Header for a move list -- second line */
2181                 /* Initial board will follow if this is a wild game */
2182
2183                 if (gameInfo.event != NULL) free(gameInfo.event);
2184                 sprintf(str, "ICS %s %s match", star_match[0], star_match[1]);
2185                 gameInfo.event = StrSave(str);
2186                 gameInfo.variant = StringToVariant(gameInfo.event);
2187                 continue;
2188             }
2189
2190             if (looking_at(buf, &i, "Move  ")) {
2191                 /* Beginning of a move list */
2192                 switch (ics_getting_history) {
2193                   case H_FALSE:
2194                     /* Normally should not happen */
2195                     /* Maybe user hit reset while we were parsing */
2196                     break;
2197                   case H_REQUESTED:
2198                     /* Happens if we are ignoring a move list that is not
2199                      * the one we just requested.  Common if the user
2200                      * tries to observe two games without turning off
2201                      * getMoveList */
2202                     break;
2203                   case H_GETTING_MOVES:
2204                     /* Should not happen */
2205                     DisplayError("Error gathering move list: nested", 0);
2206                     ics_getting_history = H_FALSE;
2207                     break;
2208                   case H_GOT_REQ_HEADER:
2209                     ics_getting_history = H_GETTING_MOVES;
2210                     started = STARTED_MOVES;
2211                     parse_pos = 0;
2212                     if (oldi > next_out) {
2213                         SendToPlayer(&buf[next_out], oldi - next_out);
2214                     }
2215                     break;
2216                   case H_GOT_UNREQ_HEADER:
2217                     ics_getting_history = H_GETTING_MOVES;
2218                     started = STARTED_MOVES_NOHIDE;
2219                     parse_pos = 0;
2220                     break;
2221                   case H_GOT_UNWANTED_HEADER:
2222                     ics_getting_history = H_FALSE;
2223                     break;
2224                 }
2225                 continue;
2226             }                           
2227             
2228             if (looking_at(buf, &i, "% ") ||
2229                 ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
2230                  && looking_at(buf, &i, "}*"))) {
2231                 savingComment = FALSE;
2232                 switch (started) {
2233                   case STARTED_MOVES:
2234                   case STARTED_MOVES_NOHIDE:
2235                     memcpy(&parse[parse_pos], &buf[oldi], i - oldi);
2236                     parse[parse_pos + i - oldi] = NULLCHAR;
2237                     ParseGameHistory(parse);
2238 #if ZIPPY
2239                     if (appData.zippyPlay && first.initDone) {
2240                         FeedMovesToProgram(&first, forwardMostMove);
2241                         if (gameMode == IcsPlayingWhite) {
2242                             if (WhiteOnMove(forwardMostMove)) {
2243                                 if (first.sendTime) {
2244                                   if (first.useColors) {
2245                                     SendToProgram("black\n", &first); 
2246                                   }
2247                                   SendTimeRemaining(&first, TRUE);
2248                                 }
2249                                 if (first.useColors) {
2250                                   SendToProgram("white\ngo\n", &first);
2251                                 } else {
2252                                   SendToProgram("go\n", &first);
2253                                 }
2254                                 first.maybeThinking = TRUE;
2255                             } else {
2256                                 if (first.usePlayother) {
2257                                   if (first.sendTime) {
2258                                     SendTimeRemaining(&first, TRUE);
2259                                   }
2260                                   SendToProgram("playother\n", &first);
2261                                   firstMove = FALSE;
2262                                 } else {
2263                                   firstMove = TRUE;
2264                                 }
2265                             }
2266                         } else if (gameMode == IcsPlayingBlack) {
2267                             if (!WhiteOnMove(forwardMostMove)) {
2268                                 if (first.sendTime) {
2269                                   if (first.useColors) {
2270                                     SendToProgram("white\n", &first);
2271                                   }
2272                                   SendTimeRemaining(&first, FALSE);
2273                                 }
2274                                 if (first.useColors) {
2275                                   SendToProgram("black\ngo\n", &first);
2276                                 } else {
2277                                   SendToProgram("go\n", &first);
2278                                 }
2279                                 first.maybeThinking = TRUE;
2280                             } else {
2281                                 if (first.usePlayother) {
2282                                   if (first.sendTime) {
2283                                     SendTimeRemaining(&first, FALSE);
2284                                   }
2285                                   SendToProgram("playother\n", &first);
2286                                   firstMove = FALSE;
2287                                 } else {
2288                                   firstMove = TRUE;
2289                                 }
2290                             }
2291                         }                       
2292                     }
2293 #endif
2294                     if (gameMode == IcsObserving && ics_gamenum == -1) {
2295                         /* Moves came from oldmoves or moves command
2296                            while we weren't doing anything else.
2297                            */
2298                         currentMove = forwardMostMove;
2299                         ClearHighlights();/*!!could figure this out*/
2300                         flipView = appData.flipView;
2301                         DrawPosition(FALSE, boards[currentMove]);
2302                         DisplayBothClocks();
2303                         sprintf(str, "%s vs. %s",
2304                                 gameInfo.white, gameInfo.black);
2305                         DisplayTitle(str);
2306                         gameMode = IcsIdle;
2307                     } else {
2308                         /* Moves were history of an active game */
2309                         if (gameInfo.resultDetails != NULL) {
2310                             free(gameInfo.resultDetails);
2311                             gameInfo.resultDetails = NULL;
2312                         }
2313                     }
2314                     HistorySet(parseList, backwardMostMove,
2315                                forwardMostMove, currentMove-1);
2316                     DisplayMove(currentMove - 1);
2317                     if (started == STARTED_MOVES) next_out = i;
2318                     started = STARTED_NONE;
2319                     ics_getting_history = H_FALSE;
2320                     break;
2321
2322                   case STARTED_OBSERVE:
2323                     started = STARTED_NONE;
2324                     SendToICS(ics_prefix);
2325                     SendToICS("refresh\n");
2326                     break;
2327
2328                   default:
2329                     break;
2330                 }
2331                 continue;
2332             }
2333             
2334             if ((started == STARTED_MOVES || started == STARTED_BOARD ||
2335                  started == STARTED_HOLDINGS ||
2336                  started == STARTED_MOVES_NOHIDE) && i >= leftover_len) {
2337                 /* Accumulate characters in move list or board */
2338                 parse[parse_pos++] = buf[i];
2339             }
2340             
2341             /* Start of game messages.  Mostly we detect start of game
2342                when the first board image arrives.  On some versions
2343                of the ICS, though, we need to do a "refresh" after starting
2344                to observe in order to get the current board right away. */
2345             if (looking_at(buf, &i, "Adding game * to observation list")) {
2346                 started = STARTED_OBSERVE;
2347                 continue;
2348             }
2349
2350             /* Handle auto-observe */
2351             if (appData.autoObserve &&
2352                 (gameMode == IcsIdle || gameMode == BeginningOfGame) &&
2353                 looking_at(buf, &i, "Game notification: * (*) vs. * (*)")) {
2354                 char *player;
2355                 /* Choose the player that was highlighted, if any. */
2356                 if (star_match[0][0] == '\033' ||
2357                     star_match[1][0] != '\033') {
2358                     player = star_match[0];
2359                 } else {
2360                     player = star_match[2];
2361                 }
2362                 sprintf(str, "%sobserve %s\n",
2363                         ics_prefix, StripHighlightAndTitle(player));
2364                 SendToICS(str);
2365
2366                 /* Save ratings from notify string */
2367                 strcpy(player1Name, star_match[0]);
2368                 player1Rating = string_to_rating(star_match[1]);
2369                 strcpy(player2Name, star_match[2]);
2370                 player2Rating = string_to_rating(star_match[3]);
2371
2372                 if (appData.debugMode)
2373                   fprintf(debugFP, 
2374                           "Ratings from 'Game notification:' %s %d, %s %d\n",
2375                           player1Name, player1Rating,
2376                           player2Name, player2Rating);
2377
2378                 continue;
2379             }
2380
2381             /* Deal with automatic examine mode after a game,
2382                and with IcsObserving -> IcsExamining transition */
2383             if (looking_at(buf, &i, "Entering examine mode for game *") ||
2384                 looking_at(buf, &i, "has made you an examiner of game *")) {
2385
2386                 int gamenum = atoi(star_match[0]);
2387                 if ((gameMode == IcsIdle || gameMode == IcsObserving) &&
2388                     gamenum == ics_gamenum) {
2389                     /* We were already playing or observing this game;
2390                        no need to refetch history */
2391                     gameMode = IcsExamining;
2392                     if (pausing) {
2393                         pauseExamForwardMostMove = forwardMostMove;
2394                     } else if (currentMove < forwardMostMove) {
2395                         ForwardInner(forwardMostMove);
2396                     }
2397                 } else {
2398                     /* I don't think this case really can happen */
2399                     SendToICS(ics_prefix);
2400                     SendToICS("refresh\n");
2401                 }
2402                 continue;
2403             }    
2404             
2405             /* Error messages */
2406             if (ics_user_moved) {
2407                 if (looking_at(buf, &i, "Illegal move") ||
2408                     looking_at(buf, &i, "Not a legal move") ||
2409                     looking_at(buf, &i, "Your king is in check") ||
2410                     looking_at(buf, &i, "It isn't your turn") ||
2411                     looking_at(buf, &i, "It is not your move")) {
2412                     /* Illegal move */
2413                     ics_user_moved = 0;
2414                     if (forwardMostMove > backwardMostMove) {
2415                         currentMove = --forwardMostMove;
2416                         DisplayMove(currentMove - 1); /* before DMError */
2417                         DisplayMoveError("Illegal move (rejected by ICS)");
2418                         DrawPosition(FALSE, boards[currentMove]);
2419                         SwitchClocks();
2420                         DisplayBothClocks();
2421                     }
2422                     continue;
2423                 }
2424             }
2425
2426             if (looking_at(buf, &i, "still have time") ||
2427                 looking_at(buf, &i, "not out of time") ||
2428                 looking_at(buf, &i, "either player is out of time") ||
2429                 looking_at(buf, &i, "has timeseal; checking")) {
2430                 /* We must have called his flag a little too soon */
2431                 whiteFlag = blackFlag = FALSE;
2432                 continue;
2433             }
2434
2435             if (looking_at(buf, &i, "added * seconds to") ||
2436                 looking_at(buf, &i, "seconds were added to")) {
2437                 /* Update the clocks */
2438                 SendToICS(ics_prefix);
2439                 SendToICS("refresh\n");
2440                 continue;
2441             }
2442
2443             if (!ics_clock_paused && looking_at(buf, &i, "clock paused")) {
2444                 ics_clock_paused = TRUE;
2445                 StopClocks();
2446                 continue;
2447             }
2448
2449             if (ics_clock_paused && looking_at(buf, &i, "clock resumed")) {
2450                 ics_clock_paused = FALSE;
2451                 StartClocks();
2452                 continue;
2453             }
2454
2455             /* Grab player ratings from the Creating: message.
2456                Note we have to check for the special case when
2457                the ICS inserts things like [white] or [black]. */
2458             if (looking_at(buf, &i, "Creating: * (*)* * (*)") ||
2459                 looking_at(buf, &i, "Creating: * (*) [*] * (*)")) {
2460                 /* star_matches:
2461                    0    player 1 name (not necessarily white)
2462                    1    player 1 rating
2463                    2    empty, white, or black (IGNORED)
2464                    3    player 2 name (not necessarily black)
2465                    4    player 2 rating
2466                    
2467                    The names/ratings are sorted out when the game
2468                    actually starts (below).
2469                 */
2470                 strcpy(player1Name, StripHighlightAndTitle(star_match[0]));
2471                 player1Rating = string_to_rating(star_match[1]);
2472                 strcpy(player2Name, StripHighlightAndTitle(star_match[3]));
2473                 player2Rating = string_to_rating(star_match[4]);
2474
2475                 if (appData.debugMode)
2476                   fprintf(debugFP, 
2477                           "Ratings from 'Creating:' %s %d, %s %d\n",
2478                           player1Name, player1Rating,
2479                           player2Name, player2Rating);
2480
2481                 continue;
2482             }
2483             
2484             /* Improved generic start/end-of-game messages */
2485             if ((tkind=0, looking_at(buf, &i, "{Game * (* vs. *) *}*")) ||
2486                 (tkind=1, looking_at(buf, &i, "{Game * (*(*) vs. *(*)) *}*"))){
2487                 /* If tkind == 0: */
2488                 /* star_match[0] is the game number */
2489                 /*           [1] is the white player's name */
2490                 /*           [2] is the black player's name */
2491                 /* For end-of-game: */
2492                 /*           [3] is the reason for the game end */
2493                 /*           [4] is a PGN end game-token, preceded by " " */
2494                 /* For start-of-game: */
2495                 /*           [3] begins with "Creating" or "Continuing" */
2496                 /*           [4] is " *" or empty (don't care). */
2497                 int gamenum = atoi(star_match[0]);
2498                 char *whitename, *blackname, *why, *endtoken;
2499                 ChessMove endtype = (ChessMove) 0;
2500
2501                 if (tkind == 0) {
2502                   whitename = star_match[1];
2503                   blackname = star_match[2];
2504                   why = star_match[3];
2505                   endtoken = star_match[4];
2506                 } else {
2507                   whitename = star_match[1];
2508                   blackname = star_match[3];
2509                   why = star_match[5];
2510                   endtoken = star_match[6];
2511                 }
2512
2513                 /* Game start messages */
2514                 if (strncmp(why, "Creating ", 9) == 0 ||
2515                     strncmp(why, "Continuing ", 11) == 0) {
2516                     gs_gamenum = gamenum;
2517                     strcpy(gs_kind, strchr(why, ' ') + 1);
2518 #if ZIPPY
2519                     if (appData.zippyPlay) {
2520                         ZippyGameStart(whitename, blackname);
2521                     }
2522 #endif /*ZIPPY*/
2523                     continue;
2524                 }
2525
2526                 /* Game end messages */
2527                 if (gameMode == IcsIdle || gameMode == BeginningOfGame ||
2528                     ics_gamenum != gamenum) {
2529                     continue;
2530                 }
2531                 while (endtoken[0] == ' ') endtoken++;
2532                 switch (endtoken[0]) {
2533                   case '*':
2534                   default:
2535                     endtype = GameUnfinished;
2536                     break;
2537                   case '0':
2538                     endtype = BlackWins;
2539                     break;
2540                   case '1':
2541                     if (endtoken[1] == '/')
2542                       endtype = GameIsDrawn;
2543                     else
2544                       endtype = WhiteWins;
2545                     break;
2546                 }
2547                 GameEnds(endtype, why, GE_ICS);
2548 #if ZIPPY
2549                 if (appData.zippyPlay && first.initDone) {
2550                     ZippyGameEnd(endtype, why);
2551                     if (first.pr == NULL) {
2552                       /* Start the next process early so that we'll
2553                          be ready for the next challenge */
2554                       StartChessProgram(&first);
2555                     }
2556                     /* Send "new" early, in case this command takes
2557                        a long time to finish, so that we'll be ready
2558                        for the next challenge. */
2559                     Reset(TRUE, TRUE);
2560                 }
2561 #endif /*ZIPPY*/
2562                 continue;
2563             }
2564
2565             if (looking_at(buf, &i, "Removing game * from observation") ||
2566                 looking_at(buf, &i, "no longer observing game *") ||
2567                 looking_at(buf, &i, "Game * (*) has no examiners")) {
2568                 if (gameMode == IcsObserving &&
2569                     atoi(star_match[0]) == ics_gamenum)
2570                   {
2571                       StopClocks();
2572                       gameMode = IcsIdle;
2573                       ics_gamenum = -1;
2574                       ics_user_moved = FALSE;
2575                   }
2576                 continue;
2577             }
2578
2579             if (looking_at(buf, &i, "no longer examining game *")) {
2580                 if (gameMode == IcsExamining &&
2581                     atoi(star_match[0]) == ics_gamenum)
2582                   {
2583                       gameMode = IcsIdle;
2584                       ics_gamenum = -1;
2585                       ics_user_moved = FALSE;
2586                   }
2587                 continue;
2588             }
2589
2590             /* Advance leftover_start past any newlines we find,
2591                so only partial lines can get reparsed */
2592             if (looking_at(buf, &i, "\n")) {
2593                 prevColor = curColor;
2594                 if (curColor != ColorNormal) {
2595                     if (oldi > next_out) {
2596                         SendToPlayer(&buf[next_out], oldi - next_out);
2597                         next_out = oldi;
2598                     }
2599                     Colorize(ColorNormal, FALSE);
2600                     curColor = ColorNormal;
2601                 }
2602                 if (started == STARTED_BOARD) {
2603                     started = STARTED_NONE;
2604                     parse[parse_pos] = NULLCHAR;
2605                     ParseBoard12(parse);
2606                     ics_user_moved = 0;
2607
2608                     /* Send premove here */
2609                     if (appData.premove) {
2610                       char str[MSG_SIZ];
2611                       if (currentMove == 0 &&
2612                           gameMode == IcsPlayingWhite &&
2613                           appData.premoveWhite) {
2614                         sprintf(str, "%s%s\n", ics_prefix,
2615                                 appData.premoveWhiteText);
2616                         if (appData.debugMode)
2617                           fprintf(debugFP, "Sending premove:\n");
2618                         SendToICS(str);
2619                       } else if (currentMove == 1 &&
2620                                  gameMode == IcsPlayingBlack &&
2621                                  appData.premoveBlack) {
2622                         sprintf(str, "%s%s\n", ics_prefix,
2623                                 appData.premoveBlackText);
2624                         if (appData.debugMode)
2625                           fprintf(debugFP, "Sending premove:\n");
2626                         SendToICS(str);
2627                       } else if (gotPremove) {
2628                         gotPremove = 0;
2629                         ClearPremoveHighlights();
2630                         if (appData.debugMode)
2631                           fprintf(debugFP, "Sending premove:\n");
2632                           UserMoveEvent(premoveFromX, premoveFromY, 
2633                                         premoveToX, premoveToY, 
2634                                         premovePromoChar);
2635                       }
2636                     }
2637
2638                     /* Usually suppress following prompt */
2639                     if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
2640                         if (looking_at(buf, &i, "*% ")) {
2641                             savingComment = FALSE;
2642                         }
2643                     }
2644                     next_out = i;
2645                 } else if (started == STARTED_HOLDINGS) {
2646                     int gamenum;
2647                     char new_piece[MSG_SIZ];
2648                     started = STARTED_NONE;
2649                     parse[parse_pos] = NULLCHAR;
2650                     if (appData.debugMode)
2651                       fprintf(debugFP, "Parsing holdings: %s\n", parse);
2652                     if (sscanf(parse, " game %d", &gamenum) == 1 &&
2653                         gamenum == ics_gamenum) {
2654                         if (gameInfo.variant == VariantNormal) {
2655                           gameInfo.variant = VariantCrazyhouse; /*temp guess*/
2656                           /* Get a move list just to see the header, which
2657                              will tell us whether this is really bug or zh */
2658                           if (ics_getting_history == H_FALSE) {
2659                             ics_getting_history = H_REQUESTED;
2660                             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
2661                             SendToICS(str);
2662                           }
2663                         }
2664                         new_piece[0] = NULLCHAR;
2665                         sscanf(parse, "game %d white [%s black [%s <- %s",
2666                                &gamenum, white_holding, black_holding,
2667                                new_piece);
2668                         white_holding[strlen(white_holding)-1] = NULLCHAR;
2669                         black_holding[strlen(black_holding)-1] = NULLCHAR;
2670 #if ZIPPY
2671                         if (appData.zippyPlay && first.initDone) {
2672                             ZippyHoldings(white_holding, black_holding,
2673                                           new_piece);
2674                         }
2675 #endif /*ZIPPY*/
2676                         if (tinyLayout || smallLayout) {
2677                             char wh[16], bh[16];
2678                             PackHolding(wh, white_holding);
2679                             PackHolding(bh, black_holding);
2680                             sprintf(str, "[%s-%s] %s-%s", wh, bh,
2681                                     gameInfo.white, gameInfo.black);
2682                         } else {
2683                             sprintf(str, "%s [%s] vs. %s [%s]",
2684                                     gameInfo.white, white_holding,
2685                                     gameInfo.black, black_holding);
2686                         }
2687                         DrawPosition(FALSE, NULL);
2688                         DisplayTitle(str);
2689                     }
2690                     /* Suppress following prompt */
2691                     if (looking_at(buf, &i, "*% ")) {
2692                         savingComment = FALSE;
2693                     }
2694                     next_out = i;
2695                 }
2696                 continue;
2697             }
2698
2699             i++;                /* skip unparsed character and loop back */
2700         }
2701         
2702         if (started != STARTED_MOVES && started != STARTED_BOARD &&
2703             started != STARTED_HOLDINGS && i > next_out) {
2704             SendToPlayer(&buf[next_out], i - next_out);
2705             next_out = i;
2706         }
2707         
2708         leftover_len = buf_len - leftover_start;
2709         /* if buffer ends with something we couldn't parse,
2710            reparse it after appending the next read */
2711         
2712     } else if (count == 0) {
2713         RemoveInputSource(isr);
2714         DisplayFatalError("Connection closed by ICS", 0, 0);
2715     } else {
2716         DisplayFatalError("Error reading from ICS", error, 1);
2717     }
2718 }
2719
2720
2721 /* Board style 12 looks like this:
2722    
2723    <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
2724    
2725  * The "<12> " is stripped before it gets to this routine.  The two
2726  * trailing 0's (flip state and clock ticking) are later addition, and
2727  * some chess servers may not have them, or may have only the first.
2728  * Additional trailing fields may be added in the future.  
2729  */
2730
2731 #define PATTERN "%72c%c%d%d%d%d%d%d%d%s%s%d%d%d%d%d%d%d%d%s%s%s%d%d"
2732
2733 #define RELATION_OBSERVING_PLAYED    0
2734 #define RELATION_OBSERVING_STATIC   -2   /* examined, oldmoves, or smoves */
2735 #define RELATION_PLAYING_MYMOVE      1
2736 #define RELATION_PLAYING_NOTMYMOVE  -1
2737 #define RELATION_EXAMINING           2
2738 #define RELATION_ISOLATED_BOARD     -3
2739 #define RELATION_STARTING_POSITION  -4   /* FICS only */
2740
2741 void
2742 ParseBoard12(string)
2743      char *string;
2744
2745     GameMode newGameMode;
2746     int gamenum, newGame, newMove, relation, basetime, increment, ics_flip = 0;
2747     int j, k, n, moveNum, white_stren, black_stren, white_time, black_time;
2748     int double_push, castle_ws, castle_wl, castle_bs, castle_bl, irrev_count;
2749     char to_play, board_chars[72];
2750     char move_str[500], str[500], elapsed_time[500];
2751     char black[32], white[32];
2752     Board board;
2753     int prevMove = currentMove;
2754     int ticking = 2;
2755     ChessMove moveType;
2756     int fromX, fromY, toX, toY;
2757     char promoChar;
2758
2759     fromX = fromY = toX = toY = -1;
2760     
2761     newGame = FALSE;
2762
2763     if (appData.debugMode)
2764       fprintf(debugFP, "Parsing board: %s\n", string);
2765
2766     move_str[0] = NULLCHAR;
2767     elapsed_time[0] = NULLCHAR;
2768     n = sscanf(string, PATTERN, board_chars, &to_play, &double_push,
2769                &castle_ws, &castle_wl, &castle_bs, &castle_bl, &irrev_count,
2770                &gamenum, white, black, &relation, &basetime, &increment,
2771                &white_stren, &black_stren, &white_time, &black_time,
2772                &moveNum, str, elapsed_time, move_str, &ics_flip,
2773                &ticking);
2774
2775     if (n < 22) {
2776         sprintf(str, "Failed to parse board string:\n\"%s\"", string);
2777         DisplayError(str, 0);
2778         return;
2779     }
2780
2781     /* Convert the move number to internal form */
2782     moveNum = (moveNum - 1) * 2;
2783     if (to_play == 'B') moveNum++;
2784     if (moveNum >= MAX_MOVES) {
2785       DisplayFatalError("Game too long; increase MAX_MOVES and recompile",
2786                         0, 1);
2787       return;
2788     }
2789     
2790     switch (relation) {
2791       case RELATION_OBSERVING_PLAYED:
2792       case RELATION_OBSERVING_STATIC:
2793         if (gamenum == -1) {
2794             /* Old ICC buglet */
2795             relation = RELATION_OBSERVING_STATIC;
2796         }
2797         newGameMode = IcsObserving;
2798         break;
2799       case RELATION_PLAYING_MYMOVE:
2800       case RELATION_PLAYING_NOTMYMOVE:
2801         newGameMode =
2802           ((relation == RELATION_PLAYING_MYMOVE) == (to_play == 'W')) ?
2803             IcsPlayingWhite : IcsPlayingBlack;
2804         break;
2805       case RELATION_EXAMINING:
2806         newGameMode = IcsExamining;
2807         break;
2808       case RELATION_ISOLATED_BOARD:
2809       default:
2810         /* Just display this board.  If user was doing something else,
2811            we will forget about it until the next board comes. */ 
2812         newGameMode = IcsIdle;
2813         break;
2814       case RELATION_STARTING_POSITION:
2815         newGameMode = gameMode;
2816         break;
2817     }
2818     
2819     /* Modify behavior for initial board display on move listing
2820        of wild games.
2821        */
2822     switch (ics_getting_history) {
2823       case H_FALSE:
2824       case H_REQUESTED:
2825         break;
2826       case H_GOT_REQ_HEADER:
2827       case H_GOT_UNREQ_HEADER:
2828         /* This is the initial position of the current game */
2829         gamenum = ics_gamenum;
2830         moveNum = 0;            /* old ICS bug workaround */
2831         if (to_play == 'B') {
2832           startedFromSetupPosition = TRUE;
2833           blackPlaysFirst = TRUE;
2834           moveNum = 1;
2835           if (forwardMostMove == 0) forwardMostMove = 1;
2836           if (backwardMostMove == 0) backwardMostMove = 1;
2837           if (currentMove == 0) currentMove = 1;
2838         }
2839         newGameMode = gameMode;
2840         relation = RELATION_STARTING_POSITION; /* ICC needs this */
2841         break;
2842       case H_GOT_UNWANTED_HEADER:
2843         /* This is an initial board that we don't want */
2844         return;
2845       case H_GETTING_MOVES:
2846         /* Should not happen */
2847         DisplayError("Error gathering move list: extra board", 0);
2848         ics_getting_history = H_FALSE;
2849         return;
2850     }
2851     
2852     /* Take action if this is the first board of a new game, or of a
2853        different game than is currently being displayed.  */
2854     if (gamenum != ics_gamenum || newGameMode != gameMode ||
2855         relation == RELATION_ISOLATED_BOARD) {
2856         
2857         /* Forget the old game and get the history (if any) of the new one */
2858         if (gameMode != BeginningOfGame) {
2859           Reset(FALSE, TRUE);
2860         }
2861         newGame = TRUE;
2862         if (appData.autoRaiseBoard) BoardToTop();
2863         prevMove = -3;
2864         if (gamenum == -1) {
2865             newGameMode = IcsIdle;
2866         } else if (moveNum > 0 && newGameMode != IcsIdle &&
2867                    appData.getMoveList) {
2868             /* Need to get game history */
2869             ics_getting_history = H_REQUESTED;
2870             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
2871             SendToICS(str);
2872         }
2873         
2874         /* Initially flip the board to have black on the bottom if playing
2875            black or if the ICS flip flag is set, but let the user change
2876            it with the Flip View button. */
2877         flipView = appData.autoFlipView ? 
2878           (newGameMode == IcsPlayingBlack) || ics_flip :
2879           appData.flipView;
2880         
2881         /* Done with values from previous mode; copy in new ones */
2882         gameMode = newGameMode;
2883         ModeHighlight();
2884         ics_gamenum = gamenum;
2885         if (gamenum == gs_gamenum) {
2886             int klen = strlen(gs_kind);
2887             if (gs_kind[klen - 1] == '.') gs_kind[klen - 1] = NULLCHAR;
2888             sprintf(str, "ICS %s", gs_kind);
2889             gameInfo.event = StrSave(str);
2890         } else {
2891             gameInfo.event = StrSave("ICS game");
2892         }
2893         gameInfo.site = StrSave(appData.icsHost);
2894         gameInfo.date = PGNDate();
2895         gameInfo.round = StrSave("-");
2896         gameInfo.white = StrSave(white);
2897         gameInfo.black = StrSave(black);
2898         timeControl = basetime * 60 * 1000;
2899         timeControl_2 = 0;
2900         timeIncrement = increment * 1000;
2901         movesPerSession = 0;
2902         gameInfo.timeControl = TimeControlTagValue();
2903         gameInfo.variant = StringToVariant(gameInfo.event);
2904         gameInfo.outOfBook = NULL;
2905         
2906         /* Do we have the ratings? */
2907         if (strcmp(player1Name, white) == 0 &&
2908             strcmp(player2Name, black) == 0) {
2909             if (appData.debugMode)
2910               fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
2911                       player1Rating, player2Rating);
2912             gameInfo.whiteRating = player1Rating;
2913             gameInfo.blackRating = player2Rating;
2914         } else if (strcmp(player2Name, white) == 0 &&
2915                    strcmp(player1Name, black) == 0) {
2916             if (appData.debugMode)
2917               fprintf(debugFP, "Remembered ratings: W %d, B %d\n",
2918                       player2Rating, player1Rating);
2919             gameInfo.whiteRating = player2Rating;
2920             gameInfo.blackRating = player1Rating;
2921         }
2922         player1Name[0] = player2Name[0] = NULLCHAR;
2923
2924         /* Silence shouts if requested */
2925         if (appData.quietPlay &&
2926             (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack)) {
2927             SendToICS(ics_prefix);
2928             SendToICS("set shout 0\n");
2929         }
2930     }
2931     
2932     /* Deal with midgame name changes */
2933     if (!newGame) {
2934         if (!gameInfo.white || strcmp(gameInfo.white, white) != 0) {
2935             if (gameInfo.white) free(gameInfo.white);
2936             gameInfo.white = StrSave(white);
2937         }
2938         if (!gameInfo.black || strcmp(gameInfo.black, black) != 0) {
2939             if (gameInfo.black) free(gameInfo.black);
2940             gameInfo.black = StrSave(black);
2941         }
2942     }
2943     
2944     /* Throw away game result if anything actually changes in examine mode */
2945     if (gameMode == IcsExamining && !newGame) {
2946         gameInfo.result = GameUnfinished;
2947         if (gameInfo.resultDetails != NULL) {
2948             free(gameInfo.resultDetails);
2949             gameInfo.resultDetails = NULL;
2950         }
2951     }
2952     
2953     /* In pausing && IcsExamining mode, we ignore boards coming
2954        in if they are in a different variation than we are. */
2955     if (pauseExamInvalid) return;
2956     if (pausing && gameMode == IcsExamining) {
2957         if (moveNum <= pauseExamForwardMostMove) {
2958             pauseExamInvalid = TRUE;
2959             forwardMostMove = pauseExamForwardMostMove;
2960             return;
2961         }
2962     }
2963     
2964     /* Parse the board */
2965     for (k = 0; k < 8; k++)
2966       for (j = 0; j < 8; j++)
2967         board[k][j] = CharToPiece(board_chars[(7-k)*9 + j]);
2968     CopyBoard(boards[moveNum], board);
2969     if (moveNum == 0) {
2970         startedFromSetupPosition =
2971           !CompareBoards(board, initialPosition);
2972     }
2973     
2974     if (ics_getting_history == H_GOT_REQ_HEADER ||
2975         ics_getting_history == H_GOT_UNREQ_HEADER) {
2976         /* This was an initial position from a move list, not
2977            the current position */
2978         return;
2979     }
2980     
2981     /* Update currentMove and known move number limits */
2982     newMove = newGame || moveNum > forwardMostMove;
2983     if (newGame) {
2984         forwardMostMove = backwardMostMove = currentMove = moveNum;
2985         if (gameMode == IcsExamining && moveNum == 0) {
2986           /* Workaround for ICS limitation: we are not told the wild
2987              type when starting to examine a game.  But if we ask for
2988              the move list, the move list header will tell us */
2989             ics_getting_history = H_REQUESTED;
2990             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
2991             SendToICS(str);
2992         }
2993     } else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
2994                || (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
2995         forwardMostMove = moveNum;
2996         if (!pausing || currentMove > forwardMostMove)
2997           currentMove = forwardMostMove;
2998     } else {
2999         /* New part of history that is not contiguous with old part */ 
3000         if (pausing && gameMode == IcsExamining) {
3001             pauseExamInvalid = TRUE;
3002             forwardMostMove = pauseExamForwardMostMove;
3003             return;
3004         }
3005         forwardMostMove = backwardMostMove = currentMove = moveNum;
3006         if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
3007             ics_getting_history = H_REQUESTED;
3008             sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
3009             SendToICS(str);
3010         }
3011     }
3012     
3013     /* Update the clocks */
3014     if (strchr(elapsed_time, '.')) {
3015       /* Time is in ms */
3016       timeRemaining[0][moveNum] = whiteTimeRemaining = white_time;
3017       timeRemaining[1][moveNum] = blackTimeRemaining = black_time;
3018     } else {
3019       /* Time is in seconds */
3020       timeRemaining[0][moveNum] = whiteTimeRemaining = white_time * 1000;
3021       timeRemaining[1][moveNum] = blackTimeRemaining = black_time * 1000;
3022     }
3023       
3024
3025 #if ZIPPY
3026     if (appData.zippyPlay && newGame &&
3027         gameMode != IcsObserving && gameMode != IcsIdle &&
3028         gameMode != IcsExamining)
3029       ZippyFirstBoard(moveNum, basetime, increment);
3030 #endif
3031     
3032     /* Put the move on the move list, first converting
3033        to canonical algebraic form. */
3034     if (moveNum > 0) {
3035         if (moveNum <= backwardMostMove) {
3036             /* We don't know what the board looked like before
3037                this move.  Punt. */
3038             strcpy(parseList[moveNum - 1], move_str);
3039             strcat(parseList[moveNum - 1], " ");
3040             strcat(parseList[moveNum - 1], elapsed_time);
3041             moveList[moveNum - 1][0] = NULLCHAR;
3042         } else if (ParseOneMove(move_str, moveNum - 1, &moveType,
3043                                 &fromX, &fromY, &toX, &toY, &promoChar)) {
3044             (void) CoordsToAlgebraic(boards[moveNum - 1],
3045                                      PosFlags(moveNum - 1), EP_UNKNOWN,
3046                                      fromY, fromX, toY, toX, promoChar,
3047                                      parseList[moveNum-1]);
3048             switch (MateTest(boards[moveNum], PosFlags(moveNum), EP_UNKNOWN)){
3049               case MT_NONE:
3050               case MT_STALEMATE:
3051               default:
3052                 break;
3053               case MT_CHECK:
3054                 strcat(parseList[moveNum - 1], "+");
3055                 break;
3056               case MT_CHECKMATE:
3057                 strcat(parseList[moveNum - 1], "#");
3058                 break;
3059             }
3060             strcat(parseList[moveNum - 1], " ");
3061             strcat(parseList[moveNum - 1], elapsed_time);
3062             /* currentMoveString is set as a side-effect of ParseOneMove */
3063             strcpy(moveList[moveNum - 1], currentMoveString);
3064             strcat(moveList[moveNum - 1], "\n");
3065         } else if (strcmp(move_str, "none") == 0) {
3066             /* Again, we don't know what the board looked like;
3067                this is really the start of the game. */
3068             parseList[moveNum - 1][0] = NULLCHAR;
3069             moveList[moveNum - 1][0] = NULLCHAR;
3070             backwardMostMove = moveNum;
3071             startedFromSetupPosition = TRUE;
3072             fromX = fromY = toX = toY = -1;
3073         } else {
3074             /* Move from ICS was illegal!?  Punt. */
3075 #if 0
3076             if (appData.testLegality && appData.debugMode) {
3077                 sprintf(str, "Illegal move \"%s\" from ICS", move_str);
3078                 DisplayError(str, 0);
3079             }
3080 #endif
3081             strcpy(parseList[moveNum - 1], move_str);
3082             strcat(parseList[moveNum - 1], " ");
3083             strcat(parseList[moveNum - 1], elapsed_time);
3084             moveList[moveNum - 1][0] = NULLCHAR;
3085             fromX = fromY = toX = toY = -1;
3086         }
3087
3088 #if ZIPPY
3089         /* Send move to chess program (BEFORE animating it). */
3090         if (appData.zippyPlay && !newGame && newMove && 
3091            (!appData.getMoveList || backwardMostMove == 0) && first.initDone) {
3092
3093             if ((gameMode == IcsPlayingWhite && WhiteOnMove(moveNum)) ||
3094                 (gameMode == IcsPlayingBlack && !WhiteOnMove(moveNum))) {
3095                 if (moveList[moveNum - 1][0] == NULLCHAR) {
3096                     sprintf(str, "Couldn't parse move \"%s\" from ICS",
3097                             move_str);
3098                     DisplayError(str, 0);
3099                 } else {
3100                     if (first.sendTime) {
3101                         SendTimeRemaining(&first, gameMode == IcsPlayingWhite);
3102                     }
3103                     SendMoveToProgram(moveNum - 1, &first);
3104                     if (firstMove) {
3105                         firstMove = FALSE;
3106                         if (first.useColors) {
3107                           SendToProgram(gameMode == IcsPlayingWhite ?
3108                                         "white\ngo\n" :
3109                                         "black\ngo\n", &first);
3110                         } else {
3111                           SendToProgram("go\n", &first);
3112                         }
3113                         first.maybeThinking = TRUE;
3114                     }
3115                 }
3116             } else if (gameMode == IcsObserving || gameMode == IcsExamining) {
3117               if (moveList[moveNum - 1][0] == NULLCHAR) {
3118                 sprintf(str, "Couldn't parse move \"%s\" from ICS", move_str);
3119                 DisplayError(str, 0);
3120               } else {
3121                 SendMoveToProgram(moveNum - 1, &first);
3122               }
3123             }
3124         }
3125 #endif
3126     }
3127
3128     if (moveNum > 0 && !gotPremove) {
3129         /* If move comes from a remote source, animate it.  If it
3130            isn't remote, it will have already been animated. */
3131         if (!pausing && !ics_user_moved && prevMove == moveNum - 1) {
3132             AnimateMove(boards[moveNum - 1], fromX, fromY, toX, toY);
3133         }
3134         if (!pausing && appData.highlightLastMove) {
3135             SetHighlights(fromX, fromY, toX, toY);
3136         }
3137     }
3138     
3139     /* Start the clocks */
3140     whiteFlag = blackFlag = FALSE;
3141     appData.clockMode = !(basetime == 0 && increment == 0);
3142     if (ticking == 0) {
3143       ics_clock_paused = TRUE;
3144       StopClocks();
3145     } else if (ticking == 1) {
3146       ics_clock_paused = FALSE;
3147     }
3148     if (gameMode == IcsIdle ||
3149         relation == RELATION_OBSERVING_STATIC ||
3150         relation == RELATION_EXAMINING ||
3151         ics_clock_paused)
3152       DisplayBothClocks();
3153     else
3154       StartClocks();
3155     
3156     /* Display opponents and material strengths */
3157     if (gameInfo.variant != VariantBughouse &&
3158         gameInfo.variant != VariantCrazyhouse) {
3159         if (tinyLayout || smallLayout) {
3160             sprintf(str, "%s(%d) %s(%d) {%d %d}", 
3161                     gameInfo.white, white_stren, gameInfo.black, black_stren,
3162                     basetime, increment);
3163         } else {
3164             sprintf(str, "%s (%d) vs. %s (%d) {%d %d}", 
3165                     gameInfo.white, white_stren, gameInfo.black, black_stren,
3166                     basetime, increment);
3167         }
3168         DisplayTitle(str);
3169     }
3170
3171    
3172     /* Display the board */
3173     if (!pausing) {
3174       
3175       if (appData.premove)
3176           if (!gotPremove || 
3177              ((gameMode == IcsPlayingWhite) && (WhiteOnMove(currentMove))) ||
3178              ((gameMode == IcsPlayingBlack) && (!WhiteOnMove(currentMove))))
3179               ClearPremoveHighlights();
3180
3181       DrawPosition(FALSE, boards[currentMove]);
3182       DisplayMove(moveNum - 1);
3183       if (appData.ringBellAfterMoves && !ics_user_moved)
3184         RingBell();
3185     }
3186
3187     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
3188 }
3189
3190 void
3191 GetMoveListEvent()
3192 {
3193     char buf[MSG_SIZ];
3194     if (appData.icsActive && gameMode != IcsIdle && ics_gamenum > 0) {
3195         ics_getting_history = H_REQUESTED;
3196         sprintf(buf, "%smoves %d\n", ics_prefix, ics_gamenum);
3197         SendToICS(buf);
3198     }
3199 }
3200
3201 void
3202 AnalysisPeriodicEvent(force)
3203      int force;
3204 {
3205     if (((programStats.ok_to_send == 0 || programStats.line_is_book)
3206          && !force) || !appData.periodicUpdates)
3207       return;
3208
3209     /* Send . command to Crafty to collect stats */
3210     SendToProgram(".\n", &first);
3211
3212     /* Don't send another until we get a response (this makes
3213        us stop sending to old Crafty's which don't understand
3214        the "." command (sending illegal cmds resets node count & time,
3215        which looks bad)) */
3216     programStats.ok_to_send = 0;
3217 }
3218
3219 void
3220 SendMoveToProgram(moveNum, cps)
3221      int moveNum;
3222      ChessProgramState *cps;
3223 {
3224     char buf[MSG_SIZ];
3225     if (cps->useUsermove) {
3226       SendToProgram("usermove ", cps);
3227     }
3228     if (cps->useSAN) {
3229       char *space;
3230       if ((space = strchr(parseList[moveNum], ' ')) != NULL) {
3231         int len = space - parseList[moveNum];
3232         memcpy(buf, parseList[moveNum], len);
3233         buf[len++] = '\n';
3234         buf[len] = NULLCHAR;
3235       } else {
3236         sprintf(buf, "%s\n", parseList[moveNum]);
3237       }
3238       SendToProgram(buf, cps);
3239     } else {
3240       /* Added by Tord: Send castle moves in "O-O" in FRC games if required by
3241        * the engine. It would be nice to have a better way to identify castle
3242        * moves here. */
3243       if(gameInfo.variant == VariantFischeRandom && cps->useOOCastle) {
3244         int fromX = moveList[moveNum][0] - 'a';
3245         int fromY = moveList[moveNum][1] - '1';
3246         int toX = moveList[moveNum][2] - 'a';
3247         int toY = moveList[moveNum][3] - '1';
3248         if((boards[currentMove][fromY][fromX] == WhiteKing
3249             && boards[currentMove][toY][toX] == WhiteRook)
3250            || (boards[currentMove][fromY][fromX] == BlackKing
3251                && boards[currentMove][toY][toX] == BlackRook)) {
3252           if(toX > fromX) SendToProgram("O-O\n", cps);
3253           else SendToProgram("O-O-O\n", cps);
3254         }
3255         else SendToProgram(moveList[moveNum], cps);
3256       }
3257       else SendToProgram(moveList[moveNum], cps);
3258       /* End of additions by Tord */
3259     }
3260 }
3261
3262 void
3263 SendMoveToICS(moveType, fromX, fromY, toX, toY)
3264      ChessMove moveType;
3265      int fromX, fromY, toX, toY;
3266 {
3267     char user_move[MSG_SIZ];
3268
3269     switch (moveType) {
3270       default:
3271         sprintf(user_move, "say Internal error; bad moveType %d (%d,%d-%d,%d)",
3272                 (int)moveType, fromX, fromY, toX, toY);
3273         DisplayError(user_move + strlen("say "), 0);
3274         break;
3275       case WhiteKingSideCastle:
3276       case BlackKingSideCastle:
3277       case WhiteQueenSideCastleWild:
3278       case BlackQueenSideCastleWild:
3279       /* PUSH Fabien */
3280       case WhiteHSideCastleFR:
3281       case BlackHSideCastleFR:
3282       /* POP Fabien */
3283         sprintf(user_move, "o-o\n");
3284         break;
3285       case WhiteQueenSideCastle:
3286       case BlackQueenSideCastle:
3287       case WhiteKingSideCastleWild:
3288       case BlackKingSideCastleWild:
3289       /* PUSH Fabien */
3290       case WhiteASideCastleFR:
3291       case BlackASideCastleFR:
3292       /* POP Fabien */
3293         sprintf(user_move, "o-o-o\n");
3294         break;
3295       case WhitePromotionQueen:
3296       case BlackPromotionQueen:
3297       case WhitePromotionRook:
3298       case BlackPromotionRook:
3299       case WhitePromotionBishop:
3300       case BlackPromotionBishop:
3301       case WhitePromotionKnight:
3302       case BlackPromotionKnight:
3303       case WhitePromotionKing:
3304       case BlackPromotionKing:
3305         sprintf(user_move, "%c%c%c%c=%c\n",
3306                 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY,
3307                 PieceToChar(PromoPiece(moveType)));
3308         break;
3309       case WhiteDrop:
3310       case BlackDrop:
3311         sprintf(user_move, "%c@%c%c\n",
3312                 ToUpper(PieceToChar((ChessSquare) fromX)),
3313                 'a' + toX, '1' + toY);
3314         break;
3315       case NormalMove:
3316       case WhiteCapturesEnPassant:
3317       case BlackCapturesEnPassant:
3318       case IllegalMove:  /* could be a variant we don't quite understand */
3319         sprintf(user_move, "%c%c%c%c\n",
3320                 'a' + fromX, '1' + fromY, 'a' + toX, '1' + toY);
3321         break;
3322     }
3323     SendToICS(user_move);
3324 }
3325
3326 void
3327 CoordsToComputerAlgebraic(rf, ff, rt, ft, promoChar, move)
3328      int rf, ff, rt, ft;
3329      char promoChar;
3330      char move[7];
3331 {
3332     if (rf == DROP_RANK) {
3333         sprintf(move, "%c@%c%c\n",
3334                 ToUpper(PieceToChar((ChessSquare) ff)), 'a' + ft, '1' + rt);
3335     } else {
3336         if (promoChar == 'x' || promoChar == NULLCHAR) {
3337             sprintf(move, "%c%c%c%c\n",
3338                     'a' + ff, '1' + rf, 'a' + ft, '1' + rt);
3339         } else {