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