1 /*
2
3 eboard - chess client
4 http://www.bergo.eng.br/eboard
5 https://github.com/fbergo/eboard
6 Copyright (C) 2000-2016 Felipe Bergo
7 fbergo/at/gmail/dot/com
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 */
24
25 #include <iostream>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <gtk/gtk.h>
29
30 #include "config.h"
31
32 #ifdef HAVE_STRINGS_H
33 #include <strings.h>
34 #endif
35
36 #include <ctype.h>
37 #include "protocol.h"
38 #include "global.h"
39 #include "chess.h"
40 #include "status.h"
41 #include "seekgraph.h"
42 #include "eboard.h"
43
44 // <b1> game 45 white [PPPPPNBBR] black [PPNN]
45
FicsProtocol()46 FicsProtocol::FicsProtocol() {
47 SetPat *sp;
48
49 global.debug("FicsProtocol","FicsProtocol");
50
51 GActions.push_back(new string("All Observers"));
52 GActions.push_back(new string("Game Info"));
53
54 PActions.push_back(new string("Finger"));
55 PActions.push_back(new string("Stats"));
56 PActions.push_back(new string("History"));
57 PActions.push_back(new string("Ping"));
58 PActions.push_back(new string("Log"));
59 PActions.push_back(new string("Variables"));
60
61 /* parsing state */
62
63 LinesReceived = 0;
64
65 AuthGone = false;
66 InitSent = false;
67 LecBotSpecial = false;
68 LastWasStyle12 = false;
69 LastWasPrompt = false;
70 LastColor = global.Colors.TextBright;
71 LastChannel = -1;
72 UseMsecs = false;
73
74 PendingStatus = 0;
75 RetrievingMoves = 0;
76 RetrievingGameList = false;
77 RetrievingAdList = false;
78
79 lastGLC=0;
80 lastALC=0;
81
82 PartnerGame = -1;
83 UserPlayingWithWhite = true;
84
85 xxnext=0;
86 xxwhen=0;
87 xxplayer[0][0]=0;
88 xxplayer[1][0]=0;
89 xxrating[0][0]=0;
90 xxrating[1][0]=0;
91
92 Login.set ("*login:*");
93 Password.set("*password:*");
94 Prompt.set("*ics%%*");
95
96 WelcomeMessage.append(new ExactStringPat("**** Starting "));
97 WelcomeMessage.append(new KleeneStarPat());
98 WelcomeMessage.append(new ExactStringPat("session as "));
99 WelcomeMessage.append(sp=new PercSSetPat());
100 WelcomeMessage.append(new KleeneStarPat());
101
102 CreateGame0.set("Creating: %S (*) %S (*)*");
103 CreateGame.set("{Game %n (%S vs. %S) Creating %s %r *}*");
104 ContinueGame.set("{Game %n (%S vs. %S) Continuing %s %r *}*");
105
106 BeginObserve.set("You are now observing game %n.");
107 EndObserve.set("*Removing game %n from observation list*");
108 ObsInfoLine.set("Game %n: %S (*) %S (*) %s %r *");
109
110 EndExamine.set("You are no longer examining game %n*");
111 MsSet.set("ms set.*");
112
113 Style12.set("<12> *");
114 Style12House.set("<b1> game %n white [*] black [*]*");
115 /* As of 2007 FICS is sending this before moves even in crazyhouse games */
116 Style12BadHouse.set("<b1> game %n white [*] black [*]%b<-*");
117
118 WhiteWon.set ("{Game %n (%S vs. %S) *} 1-0*");
119 BlackWon.set ("{Game %n (%S vs. %S) *} 0-1*");
120 Drawn.set ("{Game %n (%S vs. %S) *} 1/2-1/2*");
121 Interrupted.set ("{Game %n (%S vs. %S) *} %**");
122
123 DrawOffer.set("%S offers you a draw.*");
124
125 PrivateTell.set ("%A tells you:*");
126 //Index of new news items
127 News.set("Index of new *news items:*");
128
129 // Ns: 0=game 1=rat.white 2=rat.black
130 // Ss: 0=whitep 1=blackp
131 // *s: 1=game string
132 GameStatus.set("*%n%b%n%b%S%b%n%b%S%b[*]*");
133
134 // 2 (Exam. 2413 Species 1234 Pulga ) [ br 3 1] W: 1
135 // amazingly enough, the indexes are the same as in GameStatus
136 ExaGameStatus.set("*%n (Exam.%b%n %S%b%n %S%b)%b[*]*");
137
138 GameListEntry.set("*%n *[*]*:*");
139 EndOfGameList.set("*%n games displayed.*");
140
141 AdListEntry.set("*%n%b%n%b%r%b%n%b%n%b%r*");
142 EndOfAdList.set("*%n ad* displayed.*");
143
144 //IceBox (1156) vs. damilasa (UNR) --- Sun Apr 22, 09:07 ??? 2001
145 MovesHeader.set("%S (*) vs. %S (*) --- *");
146 //Unrated blitz match, initial time: 2 minutes, increment: 12 seconds.
147 MovesHeader2.set("%s %r match, initial time: *");
148 //Move IceBox damilasa
149 MovesHeader3.set("Move%b%S%b%S*");
150 //---- ---------------- ----------------
151 MovesHeader4.set("---- ---*");
152 // 1. d4 (0:00) d5 (0:00)
153 // n0 r0 r1 r2 r3
154 MovesBody.set("*%n.%b%r%b%r%b%r%b%r*");
155 // same thing, line without black move
156 MovesBody2.set("*%n.%b%r%b%r*");
157 // {Still in progress} *
158 MovesEnd.set("%b{*}*");
159
160 Kibitz.set ("%A*[%n]%bkibitzes:*");
161 Whisper.set ("%A*[%n]%bwhispers:*");
162 Say.set ("%A[%n] says:*");
163 Say2.set ("%A says:*");
164 ChannelTell.set("*(%n):*");
165 Seek.set("*(*) seeking*(*to respond)");
166 Shout.set ("%A shouts: *");
167 cShout.set ("%A c-shouts: *");
168 It.set ("-->*");
169 Notify.set("Notification:*");
170
171 SgClear.set("<sc>*");
172 SgRemove.set("<sr>*");
173 SgRemove2.set("Ads removed:*");
174
175 // <s> 8 w=visar ti=02 rt=2194 t=4 i=0 r=r tp=suicide c=? rr=0-9999 a=t f=f
176 // n0 s0 r0 n1 *0 n2 n3 s1 r1 r2 r3 s2 s3
177 SgAdd.set("<s>%b%n%bw=%S%bti=%r%brt=%n*%bt=%n%bi=%n%br=%s%btp=%r%bc=%r%brr=%r%ba=%s%bf=%s*");
178 SgAdd2.set("%b<s>%b%n%bw=%S%bti=%r%brt=%n*%bt=%n%bi=%n%br=%s%btp=%r%bc=%r%brr=%r%ba=%s%bf=%s*");
179
180 // bughouse stuff
181 PTell.set("%A (your partner) tells you: *");
182
183 // sample output: Your partner is playing game 75 (aglup vs. cglup).
184 GotPartnerGame.set("Your partner is playing game %n (*");
185
186 // challenge
187 Challenge.set("Challenge: *");
188
189 // FICS LectureBot fake channel tells
190 LectureBotXTell.set(":LectureBot(TD)(67):*");
191 LectureBotXTellContinued.set(": *");
192
193 // AllOb parsing
194 AllObNone.set("No one is observing game %n.*");
195
196 // Observing 18 [Fullmer vs. knighttour]: #boffo #HummerESS TieFighter (3 users)
197 // Observing 62 [VABORIS vs. Fudpucker]: BillJr(TM) CrouchingTiger(U) #Firefly
198 AllObFirstLine.set("Observing %n [*]: *");
199 AllObMidLine.set("\\*");
200 AllObState = 0;
201 memset(AllObAcc,0,1024);
202
203 memset(style12table,0,256*sizeof(piece));
204 style12table['-']=EMPTY;
205 style12table['P']=PAWN|WHITE;
206 style12table['R']=ROOK|WHITE;
207 style12table['N']=KNIGHT|WHITE;
208 style12table['B']=BISHOP|WHITE;
209 style12table['Q']=QUEEN|WHITE;
210 style12table['K']=KING|WHITE;
211 style12table['p']=PAWN|BLACK;
212 style12table['r']=ROOK|BLACK;
213 style12table['n']=KNIGHT|BLACK;
214 style12table['b']=BISHOP|BLACK;
215 style12table['q']=QUEEN|BLACK;
216 style12table['k']=KING|BLACK;
217
218 Binder.add(&WelcomeMessage,&UserName,&Login,&Password,&Prompt,
219 &CreateGame,&CreateGame0,&ContinueGame,&BeginObserve,
220 &EndObserve,&NotObserve,&EndExamine,&GameStatus,
221 &ExaGameStatus,&GameListEntry,&EndOfGameList,
222 &AdListEntry,&EndOfAdList,&Style12,&Style12House,
223 &WhiteWon,&BlackWon,&Drawn,&Interrupted,&DrawOffer,
224 &PrivateTell,&News,&MovesHeader,&MovesHeader2,
225 &MovesHeader3,&MovesHeader4,&MovesBody,&MovesBody2,
226 &MovesEnd,NULL);
227 Binder.add(&Kibitz,&Whisper,&Say,&Shout,&cShout,&It,&ChannelTell,
228 &Seek,&Notify,&SgClear,&SgAdd,&SgAdd2,&SgRemove,
229 &PTell,&Say2,&Challenge,&LectureBotXTell,
230 &LectureBotXTellContinued,&GotPartnerGame,
231 &AllObNone,&AllObFirstLine,&AllObMidLine,
232 &Style12BadHouse,&ObsInfoLine,&SgRemove2,&MsSet,NULL);
233 }
234
~FicsProtocol()235 FicsProtocol::~FicsProtocol() {
236 global.debug("FicsProtocol","~FicsProtocol");
237 clearMoveBuffer();
238 }
239
finalize()240 void FicsProtocol::finalize() {
241 global.debug("FicsProtocol","finalize");
242 if (global.network)
243 global.network->writeLine("quit");
244 if (global.IcsSeekGraph && global.skgraph2)
245 global.skgraph2->clear();
246 }
247
receiveString(char * netstring)248 void FicsProtocol::receiveString(char *netstring) {
249 ++LinesReceived;
250 parser1(netstring);
251 }
252
253 /*
254 -- eats blank lines after style-12 lines
255 -- ensures initialization is sent on non-FICS servers
256 -- ensures password mode is off on non-FICS servers
257 -- removes the prompt from the line
258 -- ignore empty lines
259 -- calls the next parser
260 */
parser1(char * T)261 bool FicsProtocol::parser1(char *T) {
262 char pstring[1024];
263
264 /* ignore blank lines from timeseal */
265 if (T[0]==0 && LastWasStyle12) {
266 LastWasStyle12 = false;
267 return true;
268 }
269
270 LastWasStyle12 = false;
271 Binder.prepare(T);
272
273 /* remove prompts */
274 while(Prompt.match()) {
275 /* avoid fake prompts in help files */
276 if (strlen(Prompt.getStarToken(0)) > 9)
277 break;
278
279 /* send initialization to servers that don't tell us our name */
280 if (!InitSent) {
281 UserName.reset();
282 UserName.append(new CIExactStringPat("i n v a l i d"));
283 strcpy(nick,"i n v a l i d");
284 sendInitialization();
285 InitSent = true;
286 }
287
288 /* exit auth mode on old ICS code */
289 if (!AuthGone) {
290 AuthGone = true;
291 global.input->setPasswordMode(0);
292 }
293
294 /* remove prompt, ignore prompt-only lines */
295 if (strlen(Prompt.getStarToken(1))>3) {
296 memset(pstring,0,1024);
297 g_strlcpy(pstring,Prompt.getStarToken(1),1024);
298 T = pstring;
299 if (T[0]==' ') T++;
300 Binder.prepare(T);
301 } else {
302 LastWasPrompt = true;
303 return true;
304 }
305
306 } /* while Prompt.match */
307
308 /* ignore empty lines after prompts (??) */
309 if (T[0]==0 && LastWasPrompt)
310 return false;
311
312 LastWasPrompt = false;
313
314 /* call next parser */
315 return(parser2(T));
316 }
317
318
319 /*
320 -- check for login-time patterns, perform console output
321 and control password obfuscation
322 -- if login is gone, calls next parser
323 */
parser2(char * T)324 bool FicsProtocol::parser2(char *T) {
325
326 if (!AuthGone) {
327 Binder.prepare(T);
328
329 if (Password.match())
330 global.input->setPasswordMode(1);
331
332 if (Login.match())
333 global.input->setPasswordMode(0);
334
335 if (WelcomeMessage.match()) {
336 global.input->setPasswordMode(0);
337 AuthGone = true;
338 g_strlcpy(nick,WelcomeMessage.getToken(3),64);
339 UserName.reset();
340 UserName.append(new KleeneStarPat());
341 UserName.append(new CIExactStringPat(nick));
342 UserName.append(new KleeneStarPat());
343
344 if (!InitSent) {
345 sendInitialization();
346 InitSent = true;
347 }
348 }
349 return(doOutput(T,-1,false,global.Colors.TextBright));
350 }
351
352 return(parser3(T));
353 }
354
355 /*
356 -- parse style-12 and zhouse stock lines, end with no output
357 -- parse seek lines, output them depending on user settings, end
358 -- parse seekinfo lines and update the seek table accordingly,
359 end with no output
360 -- call next parser
361 */
parser3(char * T)362 bool FicsProtocol::parser3(char *T) {
363
364 Binder.prepare(T);
365
366 /* style 12 strings are parsed, end with no output */
367 if (Style12.match()) {
368 parseStyle12(T);
369 LastWasStyle12 = true;
370 return true;
371 }
372
373 /* bad (early) zhouse stock lines - ignore with no output unless
374 we are playing bughouse */
375 if (Style12BadHouse.match()) {
376 attachHouseStock(Style12BadHouse,true);
377 return true;
378 }
379
380 /* zhouse stock line, parse, end with no output */
381 if (Style12House.match()) {
382 attachHouseStock(Style12House,false);
383 return true;
384 }
385
386 /* human-readable seek line */
387 if (Seek.match()) {
388
389 if (global.HideSeeks && global.IcsSeekGraph)
390 return true;
391
392 return(doOutput(T, -1, false, global.Colors.Seeks));
393 }
394
395 /* seek table/graph lines */
396 if (global.IcsSeekGraph) {
397
398 if (SgClear.match()) {
399 ensureSeekGraph();
400 global.skgraph2->clear();
401 return true;
402 }
403
404 if (SgAdd.match()) {
405 seekAdd(SgAdd);
406 return true;
407 }
408
409 if (SgAdd2.match()) {
410 seekAdd(SgAdd2);
411 return true;
412 }
413
414 if (SgRemove.match()) {
415 seekRemove(SgRemove);
416 return true;
417 }
418 } else {
419
420 if (SgClear.match()) return true;
421 if (SgAdd.match()) return true;
422 if (SgAdd2.match()) return true;
423 if (SgRemove.match()) return true;
424
425 }
426
427 // Ads Removed: msg
428 if (SgRemove2.match()) return true;
429
430 return(parser4(T));
431 }
432
433 /*
434 -- treat "Ads on Server" retrieval, end with no output
435 -- treat "Games on Server" retrieval, end with no output
436 -- treat move list retrieval, end with no output
437 -- call next parser
438 */
parser4(char * T)439 bool FicsProtocol::parser4(char *T) {
440
441 Binder.prepare(T);
442
443 /* ad list retrieval for Windows -> Ads on Server */
444 if (RetrievingAdList) {
445
446 if (AdListEntry.match()) {
447 sendAdListEntry(T);
448 return true;
449 }
450
451 if (EndOfAdList.match()) {
452 RetrievingAdList = false;
453 lastALC->endOfList();
454 lastALC = 0;
455 return true;
456 }
457 }
458
459 /* game list retrieval for Windows -> Games on Server */
460 if (RetrievingGameList) {
461
462 if (GameListEntry.match()) {
463 sendGameListEntry();
464 return true;
465 }
466
467 if (EndOfGameList.match()) {
468 RetrievingGameList = false;
469 lastGLC->endOfList();
470 lastGLC = 0;
471 return true;
472 }
473
474 }
475
476 /* move list retrieval */
477 switch(RetrievingMoves) {
478 case 0:
479 break;
480 case 1:
481 if (MovesHeader.match()) {
482 ++RetrievingMoves;
483 g_strlcpy(xxplayer[xxnext], MovesHeader.getSToken(0), 64);
484 g_strlcpy(xxrating[xxnext], MovesHeader.getStarToken(0), 64);
485 xxnext=(++xxnext)%2;
486 g_strlcpy(xxplayer[xxnext], MovesHeader.getSToken(1), 64);
487 g_strlcpy(xxrating[xxnext], MovesHeader.getStarToken(1), 64);
488 xxnext=(++xxnext)%2;
489 xxwhen = LinesReceived;
490 return true;
491 }
492 break;
493 case 2:
494 if (MovesHeader2.match()) {
495 ++RetrievingMoves;
496 return true;
497 }
498 break;
499 case 3:
500 if (MovesHeader3.match()) {
501 ++RetrievingMoves;
502 return true;
503 }
504 break;
505 case 4:
506 if (MovesHeader4.match()) {
507 ++RetrievingMoves;
508 clearMoveBuffer();
509 return true;
510 }
511 break;
512 case 5:
513 if (MovesBody.match()) { retrieve1(MovesBody); return true; }
514 if (MovesBody2.match()) { retrieve2(MovesBody2); return true; }
515 if (MovesEnd.match()) {
516 retrievef();
517
518 if (MoveRetrievals.empty())
519 RetrievingMoves = 0;
520 else
521 RetrievingMoves = 1;
522
523 return true;
524 }
525 break;
526
527 } /* switch RetrievingMoves */
528
529 return(parser5(T));
530 }
531
532 /*
533 -- treat game creation (1), echo line to console, end
534 -- treat game creation (2), end with no output
535 -- treat adjournment resume, end with no output
536 -- treat observation/examination start/end, end with no output
537 -- treat game status retrieval, end with output
538 -- treat game information line (wild/fr, wild/[012] issue)
539 -- call next parser
540 */
parser5(char * T)541 bool FicsProtocol::parser5(char *T) {
542
543 Binder.prepare(T);
544
545 if (CreateGame0.match()) {
546 //printf("c0 match\n");
547 g_strlcpy(xxplayer[xxnext],CreateGame0.getSToken(0),64);
548 g_strlcpy(xxrating[xxnext],CreateGame0.getStarToken(0),64);
549 xxnext=(++xxnext)%2;
550 g_strlcpy(xxplayer[xxnext],CreateGame0.getSToken(1),64);
551 g_strlcpy(xxrating[xxnext],CreateGame0.getStarToken(1),64);
552 xxnext=(++xxnext)%2;
553 xxwhen = LinesReceived;
554 return(doOutput(T,-1,false,global.Colors.TextBright));
555 }
556
557 if (CreateGame.match()) {
558 createGame(CreateGame);
559 return true;
560 }
561
562 if (ContinueGame.match()) {
563 createGame(ContinueGame);
564 /* fics sends 2 moves lists (!?), we ignore one of them */
565 retrieveMoves(ContinueGame);
566 return true;
567 }
568
569 if (BeginObserve.match()) {
570 addGame(BeginObserve);
571 return true;
572 }
573
574 if (EndObserve.match()) {
575 int gn;
576 ChessGame *cg;
577 gn = atoi(EndObserve.getNToken(0));
578 cg = global.getGame(gn);
579 if (cg!=NULL)
580 if (! cg->isOver())
581 removeGame(EndObserve); // if not over, user typed unob himself
582 return true;
583 }
584
585 if (EndExamine.match()) {
586 removeGame(EndExamine);
587 return true;
588 }
589
590 if (PendingStatus>0 && ExaGameStatus.match()) {
591 updateGame(ExaGameStatus);
592 --PendingStatus;
593 return(doOutput(T,-1,false,global.Colors.TextBright));
594 }
595
596 if (PendingStatus>0 && GameStatus.match()) {
597 updateGame(GameStatus);
598 --PendingStatus;
599
600 }
601
602 if (ObsInfoLine.match()) {
603 int gm;
604 ChessGame *cg;
605 gm = atoi(ObsInfoLine.getNToken(0));
606 cg = global.getGame(gm);
607 if (cg!=NULL) {
608 char *p;
609 p = ObsInfoLine.getRToken(0);
610 if (!strcmp(p,"wild/fr")) {
611 cg->Variant = WILDFR;
612 } else if (!strcmp(p,"wild/2")) {
613 cg->Variant = WILDNOCASTLE;
614 } else if ((!strcmp(p,"wild/0"))||(!strcmp(p,"wild/1"))) {
615 cg->Variant = WILDCASTLE;
616 }
617 }
618 return(doOutput(T,-1,false,global.Colors.TextBright));
619 }
620
621 return(parser6(T));
622 }
623
624 /*
625 -- treat game termination, end with output
626 -- call next parser
627 */
parser6(char * T)628 bool FicsProtocol::parser6(char *T) {
629 int tb = global.Colors.TextBright;
630
631 Binder.prepare(T);
632
633 if (WhiteWon.match()) {
634 gameOver(WhiteWon, WHITE_WIN);
635 return(doOutput(T,-1,false,tb));
636 }
637
638 if (BlackWon.match()) {
639 gameOver(BlackWon, BLACK_WIN);
640 return(doOutput(T,-1,false,tb));
641 }
642
643 if (Drawn.match()) {
644 gameOver(Drawn, DRAW);
645 return(doOutput(T,-1,false,tb));
646 }
647
648 if (Interrupted.match()) {
649 gameOver(Interrupted, UNDEF);
650 return(doOutput(T,-1,false,tb));
651 }
652
653 return(parser7(T));
654 }
655
656 /*
657 -- treat start of partner game in bughouse, end with output
658 -- treat the hidden auto-allob feature
659 -- call the next parser
660 */
parser7(char * T)661 bool FicsProtocol::parser7(char *T) {
662
663 Binder.prepare(T);
664
665 if (GotPartnerGame.match()) {
666 prepareBugPane();
667 return(doOutput(T,-1,false,global.Colors.TextBright));
668 }
669
670 if (!AllObReqs.empty() || AllObState!=0) {
671 switch(AllObState) {
672 case 0:
673 if (AllObNone.match()) {
674 if (atoi(AllObNone.getNToken(0)) == AllObReqs.back()) {
675 snprintf(AllObAcc,1024,"Game %d: no observers.",AllObReqs.back());
676 AllObReqs.pop_back();
677 global.status->setText(AllObAcc,20);
678 return true;
679 } else {
680 return(doOutput(T,-1,false,global.Colors.TextBright));
681 }
682 }
683 if (AllObFirstLine.match())
684 if (doAllOb1())
685 return true;
686 else
687 return(doOutput(T,-1,false,global.Colors.TextBright));
688 break; /* case 0 */
689 case 1:
690 if (AllObMidLine.match()) {
691 doAllOb2();
692 return true;
693 }
694 break; /* case 1 */
695 case 2:
696 if (strstr(T, "user")!=0)
697 return true;
698 if (T[0] == 0) {
699 AllObState = 3;
700 return true;
701 }
702 case 3:
703 if (strstr(T,"1 game displayed")!=0) {
704 AllObState = 0;
705 return true;
706 }
707 break;
708 }
709 } /* allob treatment */
710
711 return(parser8(T));
712 }
713
714 /*
715 -- colorize and output chat lines
716 -- treat ms ivar
717 */
parser8(char * T)718 bool FicsProtocol::parser8(char *T) {
719 int msgcolor = global.Colors.TextDefault;
720 bool personal = false;
721 ExtPatternMatcher *tm=0;
722
723 Binder.prepare(T);
724
725 if (UserName.match()) {
726 msgcolor = global.Colors.PrivateTell;
727 personal = true;
728 }
729
730 /* normal continuations */
731 if (T[0]=='\\')
732 return(doOutput(T,LastChannel,personal,personal?msgcolor:LastColor));
733
734 /* lecturebot's stupid fake channel tells */
735 if (LecBotSpecial)
736 if (LectureBotXTellContinued.match())
737 return(doOutput(T,LastChannel,false,LastColor));
738 else
739 LecBotSpecial = false;
740
741 if (Challenge.match()) global.challenged();
742
743 if (PrivateTell.match()) tm = &PrivateTell;
744 if (Say.match()) tm = &Say;
745 if (Say2.match()) tm = &Say2;
746
747 /* update chat mode command after a direct tell */
748 if (tm != 0) {
749 string x;
750 char xn[64], *xp;
751 x = "t ";
752 g_strlcpy(xn, tm->getAToken(0), 64);
753 xp = strchr(xn,'('); if (xp) *xp = 0;
754 xp = strchr(xn,'['); if (xp) *xp = 0;
755 x += xn;
756 global.ConsoleReply = x;
757 global.input->updatePrefix();
758 global.privatelyTold();
759 }
760
761 if (UserName.match()) {
762 msgcolor = global.Colors.PrivateTell;
763 personal = true;
764
765 if ( (!ChannelTell.match()) &&
766 (!LectureBotXTell.match()) )
767 return(doOutput(T,-1,personal,msgcolor));
768 }
769
770 if (LectureBotXTell.match()) {
771 LecBotSpecial = true;
772 if (!personal) msgcolor = global.Colors.ChannelTell;
773 return(doOutput(T,67,personal,msgcolor));
774 }
775
776 if (PTell.match()) {
777 global.privatelyTold();
778 global.bugpane->addBugText(PTell.getStarToken(0));
779 return(doOutput(T,-1,true,global.Colors.PrivateTell));
780 }
781
782 if (PrivateTell.match() || Say.match() || Say2.match()) {
783 return(doOutput(T,-1,true,global.Colors.PrivateTell));
784 }
785
786 if (DrawOffer.match()) {
787 global.drawOffered();
788 return(doOutput(T,-1,true,global.Colors.PrivateTell));
789 }
790
791 if (News.match() || Notify.match())
792 return(doOutput(T,-1,false,global.Colors.NewsNotify));
793
794 if (Shout.match() || cShout.match() || It.match())
795 return(doOutput(T,-1,false,global.Colors.Shouts));
796
797 if (Kibitz.match() || Whisper.match())
798 return(doOutput(T,-1,false,global.Colors.KibitzWhisper));
799
800 if (ChannelTell.match()) {
801 if (strchr(ChannelTell.getStarToken(0),' ')==0) {
802 int ch;
803 ch=atoi(ChannelTell.getNToken(0));
804 if (!personal) msgcolor = global.Colors.ChannelTell;
805 return(doOutput(T,ch,personal,msgcolor));
806 }
807 }
808
809 if (T[0]==':')
810 return(doOutput(T,-1,personal,global.Colors.Mamer));
811
812 if (MsSet.match()) {
813 UseMsecs = true;
814 return(doOutput(T,-1,personal,msgcolor));
815 }
816
817 /* nothing matched, so much CPU for nothing ;-) */
818
819 return(doOutput(T,-1,personal,msgcolor));
820 }
821
doOutput(char * msg,int channel,bool personal,int msgcolor)822 bool FicsProtocol::doOutput(char *msg, int channel,
823 bool personal, int msgcolor)
824 {
825 LastChannel = channel;
826 LastColor = msgcolor;
827
828 if (channel<0 || !global.SplitChannels) {
829 global.output->append(msg, msgcolor,
830 (personal)?IM_PERSONAL:IM_NORMAL);
831 return true;
832 }
833
834 global.appendToChannel(channel, msg, msgcolor,
835 (personal)?IM_PERSONAL:IM_NORMAL);
836
837 if (global.ChannelsToConsoleToo)
838 global.output->append(msg, msgcolor,
839 (personal)?IM_PERSONAL:IM_NORMAL);
840
841 return true;
842 }
843
createExaminedGame(int gameid,char * whitep,char * blackp)844 void FicsProtocol::createExaminedGame(int gameid,char *whitep,char *blackp) {
845 ChessGame *cg;
846 Board *b;
847 char z[64];
848
849 global.debug("FicsProtocol","createExaminedGame");
850
851 cg=new ChessGame();
852 cg->clock_regressive=1;
853 cg->GameNumber=gameid;
854 cg->StopClock=1;
855 cg->source = GS_ICS;
856 g_strlcpy(cg->PlayerName[0],whitep,64);
857 g_strlcpy(cg->PlayerName[1],blackp,64);
858 cg->MyColor=WHITE|BLACK;
859 cg->protodata[0]=258;
860
861 global.prependGame(cg);
862 b=new Board();
863 b->reset();
864 cg->setBoard(b);
865 b->setGame(cg);
866 b->setCanMove(true);
867 b->setSensitive(true);
868
869 snprintf(z,64,_("Exam.Game #%d"),cg->GameNumber);
870 global.ebook->addPage(b->widget,z,cg->GameNumber);
871 b->setNotebook(global.ebook,cg->GameNumber);
872
873 if (global.PopupSecondaryGames) {
874 b->pop();
875 b->repaint();
876 }
877
878 cg->acknowledgeInfo();
879
880 snprintf(z,64,"game %d",cg->GameNumber);
881 global.network->writeLine(z);
882 PendingStatus++;
883 }
884
sendInitialization()885 void FicsProtocol::sendInitialization() {
886 char z[128];
887 if (!InitSent) {
888
889 snprintf(z,128,"set interface eboard %s/%s\niset startpos 1\niset defprompt 1\niset ms 1",
890 global.Version,global.SystemType);
891 global.network->writeLine(z);
892 if (global.Premove)
893 global.network->writeLine("iset premove 1\n");
894 else
895 global.network->writeLine("iset premove 0\n");
896 if (global.IcsSeekGraph)
897 global.network->writeLine("iset seekinfo 1\niset seekremove 1");
898 else
899 global.network->writeLine("iset seekremove 1");
900 global.network->writeLine("iset lock 1\nstyle 12");
901 InitSent = true;
902 }
903 }
904
905 // creates a non-moveable, non-editable position for sposition
createScratchPos(int gameid,char * whitep,char * blackp)906 void FicsProtocol::createScratchPos(int gameid,char *whitep,char *blackp)
907 {
908 ChessGame *cg;
909 Board *b;
910 char z[64];
911
912 global.debug("FicsProtocol","createScratchPos");
913
914 cg=new ChessGame();
915 cg->clock_regressive=1;
916 cg->GameNumber=gameid;
917 cg->StopClock=1;
918 cg->source=GS_ICS;
919 g_strlcpy(cg->PlayerName[0],whitep,64);
920 g_strlcpy(cg->PlayerName[1],blackp,64);
921 cg->MyColor=WHITE|BLACK;
922 cg->protodata[0]=259;
923
924 global.prependGame(cg);
925 b=new Board();
926 b->reset();
927 cg->setBoard(b);
928 b->setGame(cg);
929 b->setCanMove(false);
930 b->setSensitive(false);
931
932 snprintf(z,64,_("Pos: %s vs. %s"),whitep,blackp);
933 global.ebook->addPage(b->widget,z,cg->GameNumber);
934 b->setNotebook(global.ebook,cg->GameNumber);
935
936 cg->acknowledgeInfo();
937 }
938
createGame(ExtPatternMatcher & pm)939 void FicsProtocol::createGame(ExtPatternMatcher &pm) {
940 ChessGame *cg,*rep;
941 char *p;
942
943 global.debug("FicsProtocol","createGame");
944
945 // sanity check to ignore gin lines
946 {
947 char p1[64],p2[64];
948
949 if (LinesReceived - xxwhen > 4) {
950 //printf("gin ignore 1, LR=%d xxwhen=%d\n",LinesReceived,xxwhen);
951 return;
952 }
953 memset(p1,0,64);
954 memset(p2,0,64);
955 g_strlcpy(p1,pm.getSToken(0),63);
956 g_strlcpy(p2,pm.getSToken(1),63);
957
958 if (strcmp(p1,xxplayer[0])!=0 || strcmp(p2,xxplayer[1])!=0) {
959 //printf("gin ignore 2\n");
960 return;
961 }
962 }
963
964 cg=new ChessGame();
965
966 cg->source=GS_ICS;
967 cg->clock_regressive=1;
968 cg->GameNumber=atoi(pm.getNToken(0));
969 g_strlcpy(cg->PlayerName[0],pm.getSToken(0),64);
970 g_strlcpy(cg->PlayerName[1],pm.getSToken(1),64);
971
972 // FICS sends two "Creating" strings when resuming an adjourned
973 // game. Weird.
974 rep=global.getGame(cg->GameNumber);
975 if (rep) {
976 if ( (!strcmp(cg->PlayerName[0],rep->PlayerName[0]))&&
977 (!strcmp(cg->PlayerName[1],rep->PlayerName[1]))&&
978 (rep->isFresh()) ) {
979 delete cg;
980 return;
981 }
982 }
983
984 p=knowRating(cg->PlayerName[0]);
985 if (p) g_strlcpy(cg->Rating[0],p,32);
986 p=knowRating(cg->PlayerName[1]);
987 if (p) g_strlcpy(cg->Rating[1],p,32);
988 clearRatingBuffer();
989
990 if (!strncmp(cg->PlayerName[0],nick,strlen(cg->PlayerName[0]))) {
991 cg->MyColor=WHITE;
992 UserPlayingWithWhite = true;
993 }
994 if (!strncmp(cg->PlayerName[1],nick,strlen(cg->PlayerName[1]))) {
995 cg->MyColor=BLACK;
996 UserPlayingWithWhite = false;
997 }
998
999 if (!strcmp(pm.getSToken(2),"rated"))
1000 cg->Rated=1;
1001 else
1002 cg->Rated=0;
1003
1004 cg->Variant=REGULAR;
1005 if (!strcmp(pm.getRToken(0),"crazyhouse")) cg->Variant=CRAZYHOUSE;
1006 else if (!strcmp(pm.getRToken(0),"bughouse")) cg->Variant=BUGHOUSE;
1007 else if (!strcmp(pm.getRToken(0),"suicide")) cg->Variant=SUICIDE;
1008 else if (!strcmp(pm.getRToken(0),"losers")) cg->Variant=LOSERS;
1009 else if (!strcmp(pm.getRToken(0),"atomic")) cg->Variant=ATOMIC;
1010 else if (!strncmp(pm.getRToken(0),"wild",4)) {
1011 cg->Variant=WILD;
1012 if (!strncmp(pm.getRToken(0),"wild/0",6)) cg->Variant=WILDCASTLE;
1013 if (!strncmp(pm.getRToken(0),"wild/1",6)) cg->Variant=WILDCASTLE;
1014 if (!strncmp(pm.getRToken(0),"wild/2",6)) cg->Variant=WILDNOCASTLE;
1015 if (!strncmp(pm.getRToken(0),"wild/fr",7)) cg->Variant=WILDFR;
1016 }
1017
1018 global.prependGame(cg);
1019 global.BoardList.front()->reset();
1020
1021 cg->setBoard(global.BoardList.front());
1022 global.BoardList.front()->setGame(cg);
1023 global.BoardList.front()->pop();
1024 global.BoardList.front()->setCanMove(true);
1025 global.BoardList.front()->repaint();
1026
1027 cg->acknowledgeInfo();
1028 global.status->setText(_("Game started!"),10);
1029 global.gameStarted();
1030 }
1031
addGame(ExtPatternMatcher & pm)1032 void FicsProtocol::addGame(ExtPatternMatcher &pm) {
1033 ChessGame *cg;
1034 Board *b;
1035 bool is_partner_game=false;
1036 char tab[96];
1037
1038 global.debug("FicsProtocol","addGame");
1039
1040 cg=new ChessGame();
1041
1042 cg->clock_regressive=1;
1043 cg->GameNumber=atoi(pm.getNToken(0));
1044 cg->Rated=0;
1045 cg->Variant=REGULAR;
1046 cg->source = GS_ICS;
1047
1048 global.prependGame(cg);
1049 b=new Board();
1050 b->reset();
1051 cg->setBoard(b);
1052 b->setGame(cg);
1053 b->setCanMove(false);
1054 b->setSensitive(false);
1055
1056 if (cg->GameNumber == PartnerGame) {
1057 is_partner_game=true;
1058 PartnerGame = -1;
1059 cg->protodata[2] = 1;
1060 }
1061
1062 snprintf(tab,96,_("Game #%d"),cg->GameNumber);
1063 global.ebook->addPage(b->widget,tab,cg->GameNumber);
1064 b->setNotebook(global.ebook,cg->GameNumber);
1065
1066 if (!is_partner_game)
1067 if (global.PopupSecondaryGames) {
1068 b->pop();
1069 b->repaint();
1070 }
1071
1072 cg->acknowledgeInfo();
1073
1074 // to gather info about time, variant, player names...
1075 snprintf(tab,96,"game %d",cg->GameNumber);
1076 global.network->writeLine(tab);
1077 PendingStatus++;
1078 }
1079
discardGame(int gameid)1080 void FicsProtocol::discardGame(int gameid) {
1081 ChessGame *cg;
1082 Board *b;
1083 char z[64];
1084
1085 global.debug("FicsProtocol","discardGame");
1086
1087 cg=global.getGame(gameid);
1088 if (!cg)
1089 return;
1090
1091 switch(cg->protodata[0]) {
1092 case 257: // observed
1093 snprintf(z,64,"unobserve %d",cg->GameNumber);
1094 break;
1095 case 258: // examined
1096 strcpy(z,"unexamine");
1097 break;
1098 case 259: // isolated position
1099 z[0]=0;
1100 break;
1101 default:
1102 cerr << "protodata for game " << gameid << " is " << cg->protodata[0] << endl;
1103 return;
1104 }
1105
1106 if (! cg->isOver())
1107 if (strlen(z))
1108 global.network->writeLine(z);
1109
1110 b=cg->getBoard();
1111 if (b==0) return;
1112 global.ebook->removePage(cg->GameNumber);
1113 global.removeBoard(b);
1114 delete(b);
1115 cg->GameNumber=global.nextFreeGameId(10000);
1116 cg->setBoard(0);
1117
1118 }
1119
removeGame(ExtPatternMatcher & pm)1120 void FicsProtocol::removeGame(ExtPatternMatcher &pm) {
1121 int gm;
1122 global.debug("FicsProtocol","removeGame");
1123
1124 gm=atoi(pm.getNToken(0));
1125
1126 if (global.SmartDiscard && global.ebook->isNaughty(gm))
1127 return;
1128
1129 innerRemoveGame(gm);
1130 }
1131
innerRemoveGame(int gameid)1132 void FicsProtocol::innerRemoveGame(int gameid) {
1133 Board *b;
1134 ChessGame *cg;
1135
1136 cg=global.getGame(gameid);
1137 if (cg==NULL)
1138 return;
1139
1140 // bughouse partner game ?
1141 if (cg->protodata[2])
1142 global.bugpane->stopClock();
1143
1144 b=cg->getBoard();
1145 if (b==NULL) return;
1146 global.ebook->removePage(gameid);
1147 global.removeBoard(b);
1148 delete(b);
1149 cg->GameNumber=global.nextFreeGameId(10000);
1150 cg->setBoard(0);
1151 }
1152
sendDrop(piece p,int x,int y)1153 void FicsProtocol::sendDrop(piece p,int x,int y) {
1154 piece k;
1155 char drop[5];
1156
1157 global.debug("FicsProtocol","sendDrop");
1158
1159 k=p&PIECE_MASK;
1160 drop[4]=0;
1161 switch(k) {
1162 case PAWN: drop[0]='P'; break;
1163 case ROOK: drop[0]='R'; break;
1164 case KNIGHT: drop[0]='N'; break;
1165 case BISHOP: drop[0]='B'; break;
1166 case QUEEN: drop[0]='Q'; break;
1167 default: return;
1168 }
1169 drop[1]='@';
1170 drop[2]='a'+x;
1171 drop[3]='1'+y;
1172 global.network->writeLine(drop);
1173 }
1174
sendMove(int x1,int y1,int x2,int y2,int prom)1175 void FicsProtocol::sendMove(int x1,int y1,int x2,int y2,int prom) {
1176 char move[7];
1177 piece pp;
1178
1179 global.debug("FicsProtocol","sendMove");
1180
1181 move[4]=0;
1182 move[5]=0;
1183 move[6]=0;
1184 move[0]='a'+x1;
1185 move[1]='1'+y1;
1186 move[2]='a'+x2;
1187 move[3]='1'+y2;
1188
1189 if (prom) {
1190 pp=global.promotion->getPiece();
1191 move[4]='=';
1192 switch(pp) {
1193 case QUEEN: move[5]='Q'; break;
1194 case ROOK: move[5]='R'; break;
1195 case BISHOP: move[5]='B'; break;
1196 case KNIGHT: move[5]='N'; break;
1197 case KING: move[5]='K'; break;
1198 }
1199 }
1200 global.network->writeLine(move);
1201 }
1202
1203 // attach stock info to bug/zhouse games
attachHouseStock(ExtPatternMatcher & pm,bool OnlyForBughouse)1204 void FicsProtocol::attachHouseStock(ExtPatternMatcher &pm, bool OnlyForBughouse) {
1205 ChessGame *cg;
1206 int gid;
1207 char *p;
1208 Position *pos;
1209
1210 global.debug("FicsProtocol","attachHouseStock");
1211
1212 // Style12House or Style12BadHouse
1213 // <b1> game %n white [*] black [*]*
1214 // <b1> game %n white [*] black [*]%b<-*
1215
1216 gid=atoi(pm.getNToken(0));
1217
1218 cg=global.getGame(gid);
1219 if (!cg)
1220 return;
1221
1222 if (OnlyForBughouse && cg->Variant != BUGHOUSE)
1223 return;
1224
1225 pos=&(cg->getLastPosition());
1226
1227 pos->clearStock();
1228 p=pm.getStarToken(0);
1229
1230 // cerr << "[" << p << "]\n";
1231
1232 for(;*p;p++)
1233 pos->incStockCount(style12table[*p]);
1234
1235 p=pm.getStarToken(1);
1236
1237 // cerr << "[" << p << "]\n";
1238
1239 for(;*p;p++)
1240 pos->incStockCount(style12table[tolower(*p)]);
1241
1242 cg->updateStock();
1243 }
1244
parseStyle12(char * data)1245 void FicsProtocol::parseStyle12(char *data) {
1246 static const char *sep=" \t\n";
1247 tstring t;
1248 int i,j;
1249 string *sp;
1250 Position pos;
1251 int blacktomove;
1252 int game;
1253 int rel;
1254 int itime,inc,flip;
1255 int str[2],movenum,clk[2];
1256 ChessGame *refgame;
1257 char stra[64],strb[64],strc[64];
1258 char plyr[2][64];
1259 int ackp=0;
1260 int stopclock = -1; /* -1: keep as it is, 0: stop 1: moving */
1261 bool spawnExamination;
1262 bool someSound;
1263
1264 Position prevpos;
1265
1266 global.debug("FicsProtocol","parseStyle12");
1267
1268 t.set(data);
1269 t.token(sep); // <12>
1270
1271 // the position itself
1272 for(i=7;i>=0;i--) {
1273 sp=t.token(sep);
1274 for(j=0;j<8;j++)
1275 pos.setPiece(j,i,style12table[sp->at(j)]);
1276 }
1277
1278 sp=t.token(sep); // B / W (color to move)
1279 blacktomove=(sp->at(0)=='B');
1280
1281 pos.ep[0]=t.tokenvalue(sep); // double pawn push
1282 if (blacktomove)
1283 pos.ep[1]=2;
1284 else
1285 pos.ep[1]=5;
1286
1287 // white castle short
1288 pos.maycastle[0]=t.tokenvalue(sep)?true:false;
1289
1290 // white castle long
1291 pos.maycastle[2]=t.tokenvalue(sep)?true:false;
1292
1293 // black castle short
1294 pos.maycastle[1]=t.tokenvalue(sep)?true:false;
1295
1296 // black castle long
1297 pos.maycastle[3]=t.tokenvalue(sep)?true:false;
1298
1299 t.token(sep); // mv count for draw
1300
1301 game=t.tokenvalue(sep); // game number
1302
1303 memset(plyr[0],0,64);
1304 memset(plyr[1],0,64);
1305 t.token(sep)->copy(plyr[0],63); // white's name
1306 t.token(sep)->copy(plyr[1],63); // black's name
1307
1308 rel=t.tokenvalue(sep); // relationship
1309
1310 switch(rel) {
1311 case 2: // I am the examiner of this game
1312 spawnExamination=false;
1313 refgame=global.getGame(game);
1314
1315 if (refgame==NULL)
1316 spawnExamination=true;
1317 else // v_examine=1 on FICS: played game still on main board but inactive
1318 if ( (refgame->protodata[0]!=258) && (refgame->isOver()) )
1319 spawnExamination=true;
1320
1321 if (spawnExamination)
1322 createExaminedGame(game,plyr[0],plyr[1]);
1323 break;
1324
1325 case -3: // isolated position (e.g.: sposition command)
1326 game=global.nextFreeGameId(10000);
1327 createScratchPos(game,plyr[0],plyr[1]);
1328 break;
1329 case -4: // initial position of wild game
1330 WildStartPos = pos;
1331 return;
1332 case -2: // observing examined game
1333 case -1: // I am playing, it's my opponent's turn
1334 case 1: // I am playing, it's my turn
1335 case 0: // live observation
1336 break;
1337 }
1338
1339 itime=t.tokenvalue(sep); // initial time (minutes)
1340 inc=t.tokenvalue(sep); // increment (secs)
1341
1342 str[0]=t.tokenvalue(sep); // white strength
1343 str[1]=t.tokenvalue(sep); // black strength
1344
1345 clk[0]=t.tokenvalue(sep); // white rem time
1346 clk[1]=t.tokenvalue(sep); // black rem time
1347
1348 if (!UseMsecs) {
1349 clk[0] *= 1000;
1350 clk[1] *= 1000;
1351 }
1352
1353 movenum=t.tokenvalue(sep); // move # to be made
1354
1355 t.token(sep); // verbose for previous move
1356
1357 memset(stra,0,64);
1358 memset(strb,0,64);
1359 t.token(sep)->copy(stra,63); // time taken for previous move
1360 t.token(sep)->copy(strb,63); // pretty print for previous move
1361
1362 flip=t.tokenvalue(sep); // flip board ?
1363
1364 t.setFail(-1);
1365 stopclock = t.tokenvalue(sep); // undocumented pause/unpause clock flag
1366 if (stopclock != -1)
1367 stopclock = 1-stopclock;
1368
1369 refgame=global.getGame(game);
1370 if (refgame==NULL) {
1371 cerr << "no such game: " << game << endl;
1372 return;
1373 }
1374
1375 switch(rel) {
1376 case 0: // observed
1377 case -2: // observing examined game
1378 refgame->protodata[0]=257;
1379 refgame->AmPlaying=false;
1380 break;
1381 case 1:
1382 case -1:
1383 refgame->protodata[0]=0;
1384 refgame->MyColor=(flip)?BLACK:WHITE;
1385 refgame->AmPlaying=true;
1386 if ((global.IcsSeekGraph)&&(global.skgraph2))
1387 global.skgraph2->clear();
1388 break;
1389 case 2: // examiner mode
1390 refgame->protodata[0]=258;
1391 refgame->setFree();
1392 refgame->AmPlaying=false;
1393 break;
1394 case -3: // isolated position
1395 refgame->protodata[0]=259;
1396 refgame->AmPlaying=false;
1397 break;
1398 }
1399
1400 if (strcmp(refgame->PlayerName[0],plyr[0])) {
1401 strcpy(refgame->PlayerName[0],plyr[0]);
1402 ackp=1;
1403 }
1404
1405 if (strcmp(refgame->PlayerName[1],plyr[1])) {
1406 strcpy(refgame->PlayerName[1],plyr[1]);
1407 ackp=1;
1408 }
1409
1410 if ( (refgame->timecontrol.value[0] != itime*60)||(refgame->timecontrol.value[1] != inc) ) {
1411 refgame->timecontrol.setIcs(itime*60,inc);
1412 ackp=1;
1413 }
1414
1415 if (ackp)
1416 refgame->acknowledgeInfo();
1417
1418 g_strlcat(strb," ",64);
1419 g_strlcat(strb,stra,64);
1420 snprintf(strc,64,"%d.%s%s",
1421 (blacktomove?movenum:movenum-1),
1422 (blacktomove?" ":" ... "),
1423 strb);
1424 if (strc[0]=='0')
1425 strcpy(strc,"0. startpos");
1426
1427 if ((rel==-1)&&(global.AnimateMoves))
1428 refgame->getBoard()->supressNextAnimation();
1429
1430 if (rel==-2) // don't let it tick when observing examined games
1431 refgame->StopClock=1;
1432
1433 someSound = (rel==1 || rel==0 || rel==2 || rel==-2) && (movenum!=1 || blacktomove);
1434
1435 if (stopclock >= 0)
1436 refgame->StopClock = stopclock;
1437
1438 prevpos = refgame->getLastPosition();
1439
1440 refgame->updatePosition2(pos, (movenum-(blacktomove?0:1)), blacktomove,
1441 clk[0], clk[1], strc, someSound);
1442
1443 /*
1444 if ( ((rel==-1)&&(blacktomove)) || ((rel==1)&&(!blacktomove)) ) {
1445 if (flip) cerr << "I'm white and <12> told me to flip!\n";
1446 }
1447
1448 if ( ((rel==1)&&(blacktomove)) || ((rel==-1)&&(!blacktomove)) ) {
1449 if (!flip) cerr << "I'm black and <12> told me not to flip!\n";
1450 }
1451 */
1452
1453 if (rel==1 && (movenum!=1 || blacktomove) )
1454 global.opponentMoved();
1455
1456 if (rel==0 || rel==-2 || rel==2 && (movenum!=1 || blacktomove))
1457 if (prevpos != pos)
1458 global.moveMade();
1459
1460 if (rel!=0) refgame->flipHint(flip);
1461 if (rel==1) refgame->enableMoving(true);
1462 if (rel==-1) refgame->enableMoving(false);
1463
1464 if (global.IcsAllObPlayed && (rel==1 || rel==-1) )
1465 if (refgame->protodata[3] == 0) {
1466 refgame->protodata[3] = gtk_timeout_add(30*1000, fics_allob, (void *) refgame);
1467 fics_allob((gpointer) refgame);
1468 }
1469
1470 if (global.IcsAllObObserved && (rel==0) )
1471 if (refgame->protodata[3] == 0) {
1472 refgame->protodata[3] = gtk_timeout_add(30*1000, fics_allob, (void *) refgame);
1473 fics_allob((gpointer) refgame);
1474 }
1475
1476 }
1477
updateGame(ExtPatternMatcher & pm)1478 void FicsProtocol::updateGame(ExtPatternMatcher &pm) {
1479 ChessGame *refgame;
1480 char *p,z[32];
1481 int gm;
1482
1483 global.debug("FicsProtocol","updateGame");
1484
1485 // Ns: 0=game 1=rat.white 2=rat.black
1486 // Ss: 0=whitep 1=blackp
1487 // *s: 1=game string
1488 // GameStatus.set("*%n%b%n%b%S%b%n%b%S%b[*]*");
1489 // ExaGameStatus.set("*%n (Exam.%b%n %S%b%n %S%b)%b[*]*");
1490
1491 gm=atoi(pm.getNToken(0));
1492
1493 refgame=global.getGame(gm);
1494 if (refgame==NULL) {
1495 PendingStatus++;
1496 return;
1497 }
1498
1499 g_strlcpy(refgame->PlayerName[0],pm.getSToken(0),64);
1500 g_strlcpy(refgame->PlayerName[1],pm.getSToken(1),64);
1501
1502 p=pm.getStarToken(1);
1503
1504 switch(p[1]) {
1505 case 's':
1506 case 'b':
1507 case 'l':
1508 refgame->Variant=REGULAR;
1509 break;
1510 case 'z':
1511 refgame->Variant=CRAZYHOUSE;
1512 break;
1513 case 'S':
1514 refgame->Variant=SUICIDE;
1515 break;
1516 case 'w':
1517 if (IS_NOT_WILD(refgame->Variant)) refgame->Variant=WILD;
1518 break;
1519 case 'B':
1520 refgame->Variant=BUGHOUSE;
1521 break;
1522 case 'L':
1523 refgame->Variant=LOSERS;
1524 break;
1525 case 'x':
1526 refgame->Variant=ATOMIC;
1527 break;
1528 }
1529
1530 refgame->Rated=(p[2]=='r');
1531
1532 memset(z,0,8);
1533 memcpy(z,&p[3],3);
1534
1535 refgame->timecontrol.setIcs(60*atoi(z),atoi(&p[7]));
1536 refgame->acknowledgeInfo();
1537
1538 // bughouse partner game, set up names in the bugpane
1539 if (refgame->protodata[2]!=0) {
1540 global.bugpane->setPlayerNames(refgame->PlayerName[0],
1541 refgame->PlayerName[1]);
1542 }
1543
1544 if (refgame->protodata[0]!=258) {
1545 MoveRetrievals.push_front(gm);
1546 snprintf(z,32,"moves %d",gm);
1547 global.network->writeLine(z);
1548 RetrievingMoves=1;
1549 }
1550
1551 }
1552
gameOver(ExtPatternMatcher & pm,GameResult result)1553 void FicsProtocol::gameOver(ExtPatternMatcher &pm, GameResult result) {
1554 int game;
1555 char reason[64];
1556 ChessGame *refgame;
1557
1558 global.debug("FicsProtocol","gameOver");
1559
1560 game=atoi(pm.getNToken(0));
1561 reason[63]=0;
1562 g_strlcpy(reason,pm.getStarToken(0),64);
1563
1564 refgame=global.getGame(game);
1565 if (refgame==NULL) return;
1566
1567 // allob timeout
1568 if (refgame->protodata[3]!=0) {
1569 gtk_timeout_remove(refgame->protodata[3]);
1570 refgame->protodata[3] = 0;
1571 }
1572
1573 if (refgame->protodata[0]==0) {
1574 if ( ( UserPlayingWithWhite && result==WHITE_WIN) ||
1575 ( (!UserPlayingWithWhite) && result==BLACK_WIN) )
1576 global.gameWon();
1577
1578 if ( ( UserPlayingWithWhite && result==BLACK_WIN) ||
1579 ( (!UserPlayingWithWhite) && result==WHITE_WIN) )
1580 global.gameLost();
1581 }
1582
1583 if (refgame->protodata[0]==257) {
1584 global.gameFinished();
1585 }
1586
1587 refgame->endGame(reason,result);
1588
1589 if (((refgame->protodata[0]==0)&&(global.AppendPlayed))
1590 ||
1591 ((refgame->protodata[0]==257)&&(global.AppendObserved)))
1592 {
1593 if (refgame->savePGN(global.AppendFile,true)) {
1594 char z[128];
1595 snprintf(z,128,_("Game appended to %s"),global.AppendFile);
1596 global.status->setText(z,5);
1597 }
1598 }
1599
1600 // refresh seek graph
1601 if ((global.IcsSeekGraph)&&(refgame->protodata[0]==0))
1602 refreshSeeks(true);
1603
1604 // remove board ?
1605 // if game relation is observation and user has the Smart Discard setting on:
1606 // discard now if user is not looking to the board or schedule removal to
1607 // next pane switch if user _is_ looking to the board
1608 if ((refgame->protodata[0]==257)&&(global.SmartDiscard)) {
1609 if ( global.ebook->getCurrentPageId() == refgame->GameNumber )
1610 global.ebook->setNaughty(refgame->GameNumber,true);
1611 else
1612 innerRemoveGame(refgame->GameNumber);
1613 }
1614 }
1615
refreshSeeks(bool create)1616 void FicsProtocol::refreshSeeks(bool create) {
1617 if (create) ensureSeekGraph();
1618 if ((global.IcsSeekGraph)&&(global.skgraph2)) {
1619 global.skgraph2->clear();
1620 global.network->writeLine("iset seekinfo 1");
1621 }
1622 }
1623
hasAuthenticationPrompts()1624 int FicsProtocol::hasAuthenticationPrompts() {
1625 return 1;
1626 }
1627
resign()1628 void FicsProtocol::resign() {
1629 global.network->writeLine("resign");
1630 global.status->setText(_("Resigned."),10);
1631 }
1632
draw()1633 void FicsProtocol::draw() {
1634 global.network->writeLine("draw");
1635 global.status->setText(_("<Fics Protocol> draw request sent"),30);
1636 }
1637
adjourn()1638 void FicsProtocol::adjourn() {
1639 global.network->writeLine("adjourn");
1640 global.status->setText(_("<Fics Protocol> adjourn request sent"),30);
1641 }
1642
abort()1643 void FicsProtocol::abort() {
1644 global.network->writeLine("abort");
1645 global.status->setText(_("<Fics Protocol> abort request sent"),30);
1646 }
1647
clearMoveBuffer()1648 void FicsProtocol::clearMoveBuffer() {
1649 global.debug("FicsProtocol","clearMoveBuffer");
1650 MoveBuf.clear();
1651 }
1652
retrieve1(ExtPatternMatcher & pm)1653 void FicsProtocol::retrieve1(ExtPatternMatcher &pm) {
1654 char z[32],mv[32];
1655 char tmp[2][32];
1656 int itmp;
1657 Position p,q;
1658 int gm;
1659 ChessGame *cg;
1660 variant Vr=REGULAR;
1661
1662 global.debug("FicsProtocol","retrieve1");
1663
1664 gm=MoveRetrievals.back();
1665 cg=global.getGame(gm);
1666 if (cg!=NULL) Vr=cg->Variant;
1667
1668 if (MoveBuf.empty()) {
1669 if (IS_WILD(Vr))
1670 MoveBuf.push_back(WildStartPos);
1671 else
1672 MoveBuf.push_back(Position());
1673 }
1674 p = MoveBuf.back();
1675
1676
1677 itmp=atoi(pm.getNToken(0));
1678 g_strlcpy(tmp[0],pm.getRToken(0),32);
1679 g_strlcpy(tmp[1],pm.getRToken(1),32);
1680 snprintf(z,32,"%.2d. %s %s",itmp,tmp[0],tmp[1]);
1681 g_strlcpy(mv,tmp[0],32);
1682
1683 p.moveStdNotation(mv,WHITE,Vr);
1684 p.setLastMove(z);
1685
1686 MoveBuf.push_back(p);
1687
1688 q=p;
1689
1690 itmp=atoi(pm.getNToken(0));
1691 g_strlcpy(tmp[0],pm.getRToken(2),32);
1692 g_strlcpy(tmp[1],pm.getRToken(3),32);
1693 snprintf(z,32,"%.2d. ... %s %s",itmp,tmp[0],tmp[1]);
1694 g_strlcpy(mv,tmp[0],32);
1695
1696 q.moveStdNotation(mv,BLACK,Vr);
1697 q.setLastMove(z);
1698
1699 MoveBuf.push_back(q);
1700 }
1701
retrieve2(ExtPatternMatcher & pm)1702 void FicsProtocol::retrieve2(ExtPatternMatcher &pm) {
1703 Position p;
1704 char z[32],mv[32];
1705 char tmp[2][32];
1706 int itmp, gm;
1707 ChessGame *cg;
1708 variant Vr=REGULAR;
1709
1710 global.debug("FicsProtocol","retrieve2");
1711
1712 gm=MoveRetrievals.back();
1713 cg=global.getGame(gm);
1714 if (cg!=NULL) Vr=cg->Variant;
1715
1716 if (MoveBuf.empty()) {
1717 if (IS_WILD(Vr))
1718 MoveBuf.push_back(WildStartPos);
1719 else
1720 MoveBuf.push_back(Position());
1721 }
1722 p = MoveBuf.back();
1723
1724 itmp=atoi(pm.getNToken(0));
1725 g_strlcpy(tmp[0],pm.getRToken(0),32);
1726 g_strlcpy(tmp[1],pm.getRToken(1),32);
1727 snprintf(z,32,"%.2d. %s %s",itmp,tmp[0],tmp[1]);
1728 g_strlcpy(mv,tmp[0],32);
1729
1730 p.moveStdNotation(mv,WHITE,Vr);
1731 p.setLastMove(z);
1732
1733 MoveBuf.push_back(p);
1734 }
1735
retrievef()1736 void FicsProtocol::retrievef() {
1737 int gm;
1738 ChessGame *cg;
1739 char *p;
1740
1741 global.debug("FicsProtocol","retrievef");
1742
1743 if (MoveRetrievals.empty())
1744 return;
1745
1746 gm=MoveRetrievals.back();
1747
1748 cg=global.getGame(gm);
1749 if (cg==NULL) {
1750 cerr << _("no such game: ") << gm << endl;
1751 return;
1752 }
1753 MoveRetrievals.pop_back();
1754
1755 p=knowRating(cg->PlayerName[0]);
1756 if (p) g_strlcpy(cg->Rating[0],p,32);
1757 p=knowRating(cg->PlayerName[1]);
1758 if (p) g_strlcpy(cg->Rating[1],p,32);
1759 clearRatingBuffer();
1760
1761 cg->updateGame(MoveBuf);
1762 cg->acknowledgeInfo();
1763 clearMoveBuffer();
1764 WildStartPos.setStartPos();
1765 }
1766
queryGameList(GameListConsumer * glc)1767 void FicsProtocol::queryGameList(GameListConsumer *glc) {
1768 if (RetrievingGameList) {
1769 if (glc!=lastGLC)
1770 glc->endOfList();
1771 return;
1772 }
1773
1774 RetrievingGameList=1;
1775 lastGLC=glc;
1776 global.network->writeLine("games");
1777 }
1778
queryAdList(GameListConsumer * glc)1779 void FicsProtocol::queryAdList(GameListConsumer *glc) {
1780 if (RetrievingAdList) {
1781 if (glc!=lastALC)
1782 glc->endOfList();
1783 return;
1784 }
1785 RetrievingAdList=1;
1786 lastALC=glc;
1787 global.network->writeLine("sough");
1788 }
1789
sendGameListEntry()1790 void FicsProtocol::sendGameListEntry() {
1791 char frank[256];
1792 int gn;
1793 // GameListEntry.set("*%n *[*]*:*");
1794
1795 gn=atoi(GameListEntry.getNToken(0));
1796
1797 g_strlcpy(frank,GameListEntry.getStarToken(1),256);
1798 g_strlcat(frank,"[",256);
1799 g_strlcat(frank,GameListEntry.getStarToken(2),256);
1800 g_strlcat(frank,"]",256);
1801 g_strlcat(frank,GameListEntry.getStarToken(3),256);
1802 g_strlcat(frank,":",256);
1803 g_strlcat(frank,GameListEntry.getStarToken(4),256);
1804
1805 lastGLC->appendGame(gn,frank);
1806 }
1807
sendAdListEntry(char * pat)1808 void FicsProtocol::sendAdListEntry(char *pat) {
1809 ExtPatternMatcher rpat;
1810 int gn;
1811
1812 rpat.set("*%n*");
1813 if (!rpat.match(pat))
1814 return;
1815
1816 gn=atoi(rpat.getNToken(0));
1817 lastALC->appendGame(gn,rpat.getStarToken(1));
1818 }
1819
observe(int gameid)1820 void FicsProtocol::observe(int gameid) {
1821 char z[64];
1822 snprintf(z,64,"observe %d",gameid);
1823 global.network->writeLine(z);
1824 }
1825
answerAd(int adid)1826 void FicsProtocol::answerAd(int adid) {
1827 char z[64];
1828 snprintf(z,64,"play %d",adid);
1829 global.network->writeLine(z);
1830 }
1831
knowRating(char * player)1832 char * FicsProtocol::knowRating(char *player) {
1833 int i;
1834 for(i=0;i<2;i++)
1835 if (strlen(xxplayer[i]))
1836 if ( !strncmp(xxplayer[i],player,strlen(player)) )
1837 return(xxrating[i]);
1838 return 0;
1839 }
1840
clearRatingBuffer()1841 void FicsProtocol::clearRatingBuffer() {
1842 xxplayer[0][0]=0;
1843 xxplayer[1][0]=0;
1844 }
1845
exaForward(int n)1846 void FicsProtocol::exaForward(int n) {
1847 char z[64];
1848 snprintf(z,64,"forward %d",n);
1849 global.network->writeLine(z);
1850 }
1851
exaBackward(int n)1852 void FicsProtocol::exaBackward(int n) {
1853 char z[64];
1854 snprintf(z,64,"backward %d",n);
1855 global.network->writeLine(z);
1856 }
1857
retrieveMoves(ExtPatternMatcher & pm)1858 void FicsProtocol::retrieveMoves(ExtPatternMatcher &pm) {
1859 ChessGame *refgame;
1860 int gm;
1861 char z[64];
1862 global.debug("FicsProtocol","retrieveMoves");
1863
1864 gm=atoi(pm.getNToken(0));
1865 refgame=global.getGame(gm);
1866 if (refgame==NULL)
1867 return;
1868
1869 if (refgame->protodata[0]!=258) {
1870 MoveRetrievals.push_front(gm);
1871 snprintf(z,64,"moves %d",gm);
1872 global.network->writeLine(z);
1873 RetrievingMoves=1;
1874 }
1875 }
1876
1877 // --- seek graph
1878
ensureSeekGraph()1879 void FicsProtocol::ensureSeekGraph() {
1880 if (global.skgraph2==NULL) {
1881 global.skgraph2=new SeekGraph2();
1882 global.skgraph2->show();
1883 global.ebook->addPage(global.skgraph2->widget,_("Seek Graph"),-3);
1884 global.skgraph2->setNotebook(global.ebook,-3);
1885 }
1886 }
1887
seekAdd(ExtPatternMatcher & pm)1888 void FicsProtocol::seekAdd(ExtPatternMatcher &pm) {
1889 SeekAd *ad;
1890 char *p;
1891 int flags;
1892
1893 global.debug("FicsProtocol","seekAdd");
1894
1895 // <s> 8 w=visar ti=02 rt=2194 t=4 i=0 r=r tp=suicide c=? rr=0-9999 a=t f=f
1896 // n0 s0 r0 n1 *0 n2 n3 s1 r1 r2 r3 s2 s3
1897
1898 ensureSeekGraph();
1899 ad=new SeekAd();
1900
1901 // %n
1902 ad->id = atoi(pm.getNToken(0));
1903 ad->rating = pm.getNToken(1);
1904 ad->clock = atoi(pm.getNToken(2));
1905 ad->incr = atoi(pm.getNToken(3));
1906
1907 p=pm.getStarToken(0);
1908 if ((p[0]=='P')&&(atoi(ad->rating.c_str())==0)) ad->rating="++++";
1909
1910 // %s
1911 ad->player = pm.getSToken(0);
1912
1913 p=pm.getSToken(1);
1914 if (p[0]=='r') ad->rated=true;
1915
1916 ad->kind = pm.getRToken(1);
1917
1918 // %r
1919 flags = strtol(pm.getRToken(0),NULL,16);
1920
1921 ad->flags=" ";
1922
1923 if (flags&0x01) ad->flags += "(U)";
1924 if (flags&0x02) ad->flags += "(C)";
1925 if (flags&0x04) ad->flags += "(GM)";
1926 if (flags&0x08) ad->flags += "(IM)";
1927 if (flags&0x10) ad->flags += "(FM)";
1928 if (flags&0x20) ad->flags += "(WGM)";
1929 if (flags&0x40) ad->flags += "(WIM)";
1930 if (flags&0x80) ad->flags += "(WFM)";
1931
1932 p=pm.getRToken(2);
1933 switch(p[0]) {
1934 case '?': ad->color=" "; break;
1935 case 'W': ad->color=_("white"); break;
1936 case 'B': ad->color=_("black"); break;
1937 }
1938
1939 ad->range=pm.getRToken(3);
1940
1941 p=pm.getSToken(2); if (p[0]=='t') ad->automatic=true;
1942 p=pm.getSToken(3); if (p[0]=='t') ad->formula=true;
1943
1944 global.skgraph2->add(ad);
1945 }
1946
seekRemove(ExtPatternMatcher & pm)1947 void FicsProtocol::seekRemove(ExtPatternMatcher &pm) {
1948 tstring t;
1949 int i;
1950 t.setFail(-1);
1951 t.set(pm.getStarToken(0));
1952 ensureSeekGraph();
1953 while( (i=t.tokenvalue(" \t\n")) >= 0 ) {
1954 global.skgraph2->remove(i);
1955 }
1956 }
1957
getPlayerActions()1958 vector<string *> * FicsProtocol::getPlayerActions() {
1959 return(&PActions);
1960 }
1961
getGameActions()1962 vector<string *> * FicsProtocol::getGameActions() {
1963 return(&GActions);
1964 }
1965
callPlayerAction(char * player,string * action)1966 void FicsProtocol::callPlayerAction(char *player, string *action) {
1967 for(unsigned int i=0;i<PActions.size();i++)
1968 if ( (*PActions[i]) == (*action) ) { doPlayerAction(player, i); break; }
1969 }
1970
callGameAction(int gameid,string * action)1971 void FicsProtocol::callGameAction(int gameid, string *action) {
1972 for(unsigned int i=0;i<GActions.size();i++)
1973 if ( (*GActions[i]) == (*action) ) { doGameAction(gameid, i); break; }
1974 }
1975
doPlayerAction(char * player,int id)1976 void FicsProtocol::doPlayerAction(char *player, int id) {
1977 char z[256],w[256];
1978 bool ok=true;
1979
1980 switch(id) {
1981 case 0: snprintf(z,256,"finger %s",player); break;
1982 case 1: snprintf(z,256,"stat %s",player); break;
1983 case 2: snprintf(z,256,"history %s",player); break;
1984 case 3: snprintf(z,256,"ping %s",player); break;
1985 case 4: snprintf(z,256,"log %s",player); break;
1986 case 5: snprintf(z,256,"var %s",player); break;
1987 default: ok=false;
1988 }
1989
1990 if (ok) {
1991 snprintf(w,256,_("> [issued from menu] %s"),z);
1992 global.output->append(w,global.SelfInputColor);
1993 global.network->writeLine(z);
1994 }
1995 }
1996
doGameAction(int gameid,int id)1997 void FicsProtocol::doGameAction(int gameid, int id) {
1998 char z[256], w[256];
1999 bool ok=true;
2000
2001 switch(id) {
2002 case 0: snprintf(z,256,"allob %d",gameid); break;
2003 case 1: snprintf(z,256,"ginfo %d",gameid); break;
2004 default: ok=false;
2005 }
2006
2007 if (ok) {
2008 snprintf(w,256,_("> [issued from menu] %s"),z);
2009 global.output->append(w,global.SelfInputColor);
2010 global.network->writeLine(z);
2011 }
2012 }
2013
prepareBugPane()2014 void FicsProtocol::prepareBugPane() {
2015 char z[64];
2016 PartnerGame = atoi(GotPartnerGame.getNToken(0));
2017 snprintf(z,64,"observe %d",PartnerGame);
2018 global.network->writeLine(z);
2019 }
2020
updateVar(ProtocolVar pv)2021 void FicsProtocol::updateVar(ProtocolVar pv) {
2022 char z[256];
2023 switch(pv) {
2024 case PV_premove:
2025 snprintf(z,256,"iset premove %d\n",global.Premove ? 1 : 0);
2026 global.network->writeLine(z);
2027 break;
2028 default:
2029 break;
2030 }
2031 }
2032
fics_allob(gpointer data)2033 gboolean fics_allob(gpointer data) {
2034 ChessGame *cg = (ChessGame *) data;
2035 FicsProtocol *proto;
2036 char z[64];
2037
2038 if (global.protocol == 0) return FALSE;
2039 if (cg == 0) return FALSE;
2040 if (cg->protodata[3] == 0) return FALSE;
2041 if (cg->isOver()) return FALSE;
2042 if (cg->source != GS_ICS) return FALSE;
2043 if (global.network == 0) return FALSE;
2044
2045 proto = (FicsProtocol *) (global.protocol);
2046 proto->AllObReqs.push_front(cg->GameNumber);
2047
2048 snprintf(z,64,"allob %d",cg->GameNumber);
2049 global.network->writeLine(z);
2050
2051 return TRUE;
2052 }
2053
2054 // AllObFirstLine Matched
doAllOb1()2055 bool FicsProtocol::doAllOb1() {
2056 int n,i;
2057 tstring t;
2058 string *p;
2059 bool islast = false;
2060
2061 // cerr << "allob2\n";
2062
2063 n = atoi(AllObFirstLine.getNToken(0));
2064
2065 // cerr << "n0=[" << AllObFirstLine.getNToken(0) << "]\n";
2066 // cerr << "n=" << n << ", back=" << AllObReqs.back() << endl;
2067 if (n!=AllObReqs.back())
2068 return false;
2069
2070 t.set(AllObFirstLine.getStarToken(1));
2071
2072 snprintf(AllObAcc,1024,"Observing game %d: ",n);
2073 i=0;
2074 while((p=t.token(" "))!=0) {
2075
2076 if ( (*p)[0] == '(' ) {
2077 islast = true;
2078 break;
2079 }
2080
2081 if(i!=0) strcat(AllObAcc,", ");
2082 g_strlcat(AllObAcc,p->c_str(),1024);
2083 ++i;
2084
2085 // cerr << "partial: [" << AllObAcc << "]\n";
2086
2087 if (strlen(AllObAcc) > 128) {
2088 g_strlcat(AllObAcc,"...",1024);
2089 break;
2090 }
2091 }
2092
2093 if (islast) {
2094 AllObState = 2;
2095 global.status->setText(AllObAcc,28);
2096 AllObReqs.pop_back();
2097 } else
2098 AllObState = 1;
2099
2100 return true;
2101 }
2102
2103 // AllObMidLine matched
doAllOb2()2104 void FicsProtocol::doAllOb2() {
2105 int i;
2106 bool islast = false;
2107 tstring t;
2108 string *p;
2109
2110 t.set(AllObFirstLine.getStarToken(0));
2111
2112 while((p=t.token(" "))!=0) {
2113
2114 if ( (*p)[0] == '(' ) {
2115 islast = true;
2116 break;
2117 }
2118 if (strlen(AllObAcc) > 128)
2119 continue;
2120
2121 g_strlcat(AllObAcc,", ",1024);
2122 g_strlcat(AllObAcc,p->c_str(),1024);
2123 ++i;
2124 if (strlen(AllObAcc) > 128) {
2125 g_strlcat(AllObAcc,"...",1024);
2126 break;
2127 }
2128 }
2129
2130 if (islast) {
2131 AllObState = 2;
2132 global.status->setText(AllObAcc,28);
2133 AllObReqs.pop_back();
2134 } else
2135 AllObState = 1;
2136
2137 }
2138