home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 035 / pmics.zip / wcomm.cc < prev    next >
C/C++ Source or Header  |  1994-12-13  |  34KB  |  1,052 lines

  1. /*
  2.     PMICS -- PM interface for playing chess on internet chess server
  3.     Copyright (C) 1994  Kevin Nomura
  4.  
  5.     This program is free software; you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation; either version 2 of the License, or
  8.     (at your option) any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.     Author can be reached at email: chow@netcom.com
  20. */
  21. #include "wcomm.hh"
  22. #include "game.hh"
  23. #include "pmics.hh"
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <ithread.hpp>
  27. #include <icolor.hpp>
  28. #include <ievtdata.hpp>
  29. #include <imsgbox.hpp>
  30. #include <ifont.hpp>        // for font dialog
  31. #include <ifontdlg.hpp>
  32. #include <ireslib.hpp>
  33. //#define INCL_WINMESSAGEMGR
  34. #define INCL_WIN
  35. #include <os2.h>
  36. #include <strstrea.h>
  37. #include "session.hh"
  38. #include "engcom.hh"
  39. #include "dlgmatch.h"
  40.  
  41. #define ICS_PROMPT "aics% "
  42.  
  43. extern EngineComm *engineComm;
  44. extern ASession *aSession;
  45. extern int getIntOption(IString s, int def);
  46. extern IString getStringOption(IString s, IString def);
  47. void icshook(IString &);
  48. IWindowHandle hComm;
  49. strstream commStream;
  50.  
  51. static void cancelBackspaces (IString &);
  52.  
  53. void debugmsg(char *s, IWindow *w)
  54. {
  55.   IMessageBox mb(w);
  56.   mb.setTitle("debug msg");
  57.     mb.show(s, IMessageBox::informationIcon |
  58.         IMessageBox::yesNoCancelButton |
  59.         IMessageBox::applicationModal |
  60.         IMessageBox::moveable);
  61. }
  62.  
  63. /**************************************************************************
  64.  * FUNCTION:    constructor (class CommWindow)
  65.  *
  66.  * DESCRIPTION: Build comm window.  Uses IComboBox to simulate a
  67.  *   scrollable terminal window.
  68.  *               
  69.  **************************************************************************/
  70. CommWindow::CommWindow(unsigned long windowId, //Constructor for this class
  71.                IWindow *parent) :
  72.              IListBox (windowId, parent, parent, IRectangle(),
  73.             (IListBox::classDefaultStyle |
  74.              IControl::tabStop) &
  75.             ~(IListBox::horizontalScroll |
  76.               IListBox::extendedSelect |
  77.               IListBox::multipleSelect))
  78. {
  79.   IFUNCTRACE_DEVELOP();
  80.   hComm = handle();        // export commWindow handle
  81.  
  82.   /******************************************
  83.    * attach IKeyboardHandler for keypresses
  84.    ******************************************/
  85.   handleEventsFor(this);
  86.  
  87.   /*****************************************
  88.    * attach IEditHandler to see what it does
  89.    ****************************************/
  90. /*  editHandler = new CommEditHandler;
  91.   editHandler->handleEventsFor(this);*/
  92.  
  93.   /**************************************
  94.    * attach IHandler for general events
  95.    **************************************/
  96.   msgHandler = new CommMsgHandler(this);
  97.   msgHandler->handleEventsFor(this);
  98.  
  99.   commFilter = new ACommFilterStyle12;
  100.  
  101.   localecho = getIntOption("localecho", 0) != 0;
  102.  
  103. //  setColor(IEntryField::background, IColor(0xff,0xff,0xcc));
  104.   setFont(IFont(getStringOption("commfont", "System VIO"),
  105.         getIntOption("commfontsize", 14)));
  106.  
  107.   show();
  108. } /* end PmicsWindow :: PmicsWindow(...) */
  109.  
  110.  
  111. Boolean CommWindow::key (IKeyboardEvent &evt)
  112. {
  113.   char c,s[2];
  114.   int  i;
  115.  
  116.   if (evt.isUpTransition()) {
  117.     return false;
  118.   }
  119.   IFUNCTRACE_DEVELOP();        // bypassed on up transition
  120.  
  121.   /********************************************************************
  122.    * keyhandler screws up for control chars.  they don't show up
  123.    * as character events, but only as scancode events: and who wants
  124.    * to fool with scancodes?  we hafta drop down to PM and dig the char
  125.    * out of the event data, sigh.
  126.    ********************************************************************/
  127.   if (evt.isCtrlDown()) {
  128.     ITRACE_DEVELOP("control is down");
  129.     if (evt.isVirtual() && evt.virtualKey() == IKeyboardEvent::ctrl)
  130.       return false;        // the ctrl key itself
  131.     c = IEventData(evt.parameter2()).char1();
  132.     ITRACE_DEVELOP("control pressed, data is: " + IString(c)+IString((int)c));
  133.     if (c>='a' && c<='z')
  134.       c = c - 'a' + 1;
  135.     s[0] = c;
  136.     s[1] = 0;
  137.     for (i = evt.isRepeat() ? evt.repeatCount() : 1; i; i--)
  138.       aSession->write(s);
  139.   }
  140.  
  141.   if (evt.isVirtual()) {
  142.     ITRACE_DEVELOP("vkey: " + IString((unsigned short)(evt.virtualKey())) +
  143.            " *" + IString(evt.repeatCount()));
  144.     switch(evt.virtualKey()) {
  145.     case IKeyboardEvent::backSpace:
  146.       c = (bs2del) ? 127 : 8;
  147.       ITRACE_DEVELOP("BS: deleting last character with " + IString((int)c));
  148.       s[0] = c;
  149.       s[1] = 0;
  150.       for (i = evt.isRepeat() ? evt.repeatCount() : 1; i; i--)
  151.     aSession->write(s);
  152.       return true;
  153.  
  154.     case IKeyboardEvent::newLine:
  155.       c = (cr2lf) ? 10 : 13;
  156.       ITRACE_DEVELOP("CR: newLine maps to " + IString((int)c));
  157.       s[0] = c;
  158.       s[1] = 0;
  159.       for (i = evt.isRepeat() ? evt.repeatCount() : 1; i; i--)
  160.     aSession->write(s);
  161.       return true;
  162.     }
  163.     //return false;        // use default semantics for arrow keys, etc.
  164.     // need fall thru for chars
  165.   }
  166.  
  167.   if (evt.isCharacter()) {
  168.     ITRACE_DEVELOP("key: " + IString((unsigned short)(evt.character())));
  169.     if (evt.isRepeat() || evt.repeatCount() > 0)
  170.       ITRACE_DEVELOP("repeatCount = "+IString(evt.repeatCount())+
  171.              "isRepeat = "+IString(evt.repeatCount()));
  172.     c = evt.character();
  173.     if (evt.isCtrlDown()) {
  174.       ITRACE_DEVELOP("   control: " + IString(c) + "=" + IString((int)c));
  175.       if (c>='a' && c<='z')
  176.     c = c - 'a' + 1;
  177.     }
  178.     
  179.     /*if (c == '\r') {
  180.       c = '\n';
  181.       ITRACE_DEVELOP("<CR> -> <LF>");
  182.       }*/
  183.     
  184.     s[0] = c;
  185.     s[1] = 0;
  186.     for (i = evt.isRepeat() ? evt.repeatCount() : 1; i; i--)
  187.       aSession->write(s);
  188.     timerTick(0);       // reset idle counter
  189.     
  190.     /********************************************************
  191.      * implement local echo
  192.      ********************************************************/
  193.     if (localecho) {
  194.       int  i = count() - 1;
  195.       setItemText(i, itemText(i) + IString(c));
  196.     }
  197.  
  198.     return true;   // suppress listbox jump to line with first letter
  199.   }
  200.   return false;
  201. }
  202.  
  203.  
  204. /**************************************************************************
  205.  * FUNCTION:    lookForChallenge (class CommWindow)
  206.  *
  207.  * DESCRIPTION: Recognize a match request from another ICS player, and
  208.  *   pop up a requester that notifies the user and lets them accept,
  209.  *   reject or ignore the match request.  Example challenge line:
  210.  *
  211.  *   Challenge: DaveS (2068) [white] woof (1813) [un]rated blitz|wild(n)|standard 3 12 [5 0]
  212.  *
  213.  **************************************************************************/
  214. void CommWindow::lookForChallenge (IString &line)
  215. {
  216.   int    i, oppName, wildType, firstClock, secondClock;
  217.   extern IWindow *mainWin;
  218.   Boolean isRated, isWhite, isBlack;
  219.  
  220.   if (line.subString(1,10) != "Challenge:")
  221.     return;
  222.  
  223.   // parse as blank-separated tokens, starting after "Challenge:"
  224.   i = 2;
  225.   isWhite = isBlack = isRated = false;
  226.   wildType = 0;
  227.  
  228.   oppName = i;
  229.   i++; i++;            // consume opponent name and rating
  230.   if (line.word(i)[1] == '[') {    // color specification
  231.     if (line.word(i) == "[white]") 
  232.       isWhite = true;
  233.     else if (line.word(i) == "[black]")
  234.       isBlack = true;
  235.     i++;            // consume color specification
  236.   }
  237.   i++; i++;            // consume my name and rating
  238.   isRated = (line.word(i) == "rated");
  239.   i++;                // consume rated specification
  240.   if (line.word(i).subString(1,4) == "wild") {
  241.     sscanf(line.word(i), "wild(%d)", &wildType);
  242.   }
  243.   i++;                // consume blitz/standard/wild specification
  244.   firstClock = i;
  245.   i++; i++;            // consume 1st time specification
  246.   secondClock = 0;
  247.   if (line.numWords() >= i+1) {
  248.     secondClock = i;
  249.     i++; i++;            // consume odds time specification
  250.   }
  251.   
  252.   ITRACE_DEVELOP("challenge: <"+line+">");
  253.   AMatchDialog  *dw = new AMatchDialog(IWindow::desktopWindow(),
  254.                        mainWin,
  255.                        this,
  256.                        line.words(oppName,2),
  257.                        isRated,
  258.                        isWhite,
  259.                        isBlack,
  260.                        wildType,
  261.                        line.words(firstClock,2),
  262.                        secondClock ? line.words(secondClock,2) : IString(""));
  263. }
  264.  
  265. /**************************************************************************
  266.  * FUNCTION:    fontDialog (class CommWindow)
  267.  *
  268.  * DESCRIPTION: Let the user select a new text font for the comm window.
  269.  *
  270.  **************************************************************************/
  271. void CommWindow::fontDialog()
  272. {
  273.   if (0)
  274.   {
  275.     // rated blitz
  276.     IString s = "Challenge: caesar (1669) woof (1680) rated blitz 3 12";
  277.     lookForChallenge(s);
  278.  
  279.     // unrated wild
  280.     s = "Challenge: caesar (1669) woof (1680) unrated wild(5) 3 12";
  281.     lookForChallenge(s);
  282.  
  283.     // rated wild 14, white, with time odds
  284.     s = "Challenge: caesar (1669) [black] woof (1680) rated wild(14) 3 12 1 0";
  285.     lookForChallenge(s);
  286.   }
  287.  
  288.   IFont curfont(this);
  289.   IFontDialog::Settings fsettings(&curfont);
  290.   IFontDialog fd(IWindow::desktopWindow(),IWindow::desktopWindow(), fsettings);
  291.  
  292.   if (fd.pressedOK())
  293.     setFont(curfont);
  294. }
  295.  
  296.  
  297. /**************************************************************************
  298.  * FUNCTION:    timerTick (class CommWindow)
  299.  *
  300.  * DESCRIPTION:  Send a key to the UNIX host every 3 minutes to prevent 
  301.  *   idle detection.  Driven by 1-second interval timer tick (we share
  302.  *   the timer in PmicsWindow).
  303.  *
  304.  * ================ MAKE CONFIGURABLE =======================
  305.  *
  306.  **************************************************************************/
  307. void CommWindow::timerTick(int setCount)
  308. {
  309.   static int count = 0;
  310.   if (setCount >= 0)
  311.     count = setCount;
  312.   else
  313.     count++;
  314.   if ((count > 0) && (count % 360 == 0)) {
  315.     ITRACE_DEVELOP("CommWindow::timerTick send keepalive char, count="+
  316.            IString(count));
  317.     aSession->write(" ");
  318.   }
  319. }
  320.  
  321.  
  322. /**************************************************************************
  323.  * FUNCTION:    edit (class CommEditHandler)
  324.  *
  325.  * DESCRIPTION: This handler is supposed to intercept editing keys
  326.  *   inside the IComboBox.
  327.  *               
  328.  **************************************************************************/
  329. Boolean CommEditHandler :: edit (IControlEvent &evt)
  330. {
  331.   IFUNCTRACE_DEVELOP();
  332.   char s[2], c;
  333.       
  334.   ITRACE_DEVELOP(IString("char1: ") +
  335.          IString((unsigned short)(evt.parameter1().char1())) +
  336.          IString(" char2: ") +
  337.          IString((unsigned short)(evt.parameter1().char2())) +
  338.          IString(" char3: ") +
  339.          IString((unsigned short)(evt.parameter1().char3())) +
  340.          IString(" char4: ") +
  341.          IString((unsigned short)(evt.parameter1().char4())));
  342.   ITRACE_DEVELOP(IString("char1: ") +
  343.          IString((unsigned short)(evt.parameter2().char1())) +
  344.          IString(" char2: ") +
  345.          IString((unsigned short)(evt.parameter2().char2())) +
  346.          IString(" char3: ") +
  347.          IString((unsigned short)(evt.parameter2().char3())) +
  348.          IString(" char4: ") +
  349.          IString((unsigned short)(evt.parameter2().char4())) +
  350.          IString(" *") + 
  351.          IString((unsigned short)(evt.parameter1().char3())));
  352.   if (!(evt.parameter1() & KC_KEYUP)) {
  353.     ITRACE_DEVELOP("keydown event");
  354.   }
  355.   if ((evt.parameter1() & KC_CTRL)) {
  356.     ITRACE_DEVELOP("ctrl event");
  357.   }
  358.  
  359.   return false;
  360. }
  361.  
  362.  
  363.  
  364. /**************************************************************************
  365.  * FUNCTION:    dispatchHandlerEvent (class CommMsgHandler)
  366.  *
  367.  * DESCRIPTION: handler for general window messages
  368.  *
  369.  * MESSAGES:    MSG_COM_IN - arbitrary-sized block of incoming data
  370.  *              MSG_COM_FONT - user requested to change comm window font
  371.  *               
  372.  **************************************************************************/
  373. Boolean CommMsgHandler::dispatchHandlerEvent (IEvent &evt)
  374. {
  375. //  IFUNCTRACE_DEVELOP();
  376.   CommWindow      *cwin = (CommWindow *)evt.window();
  377.  
  378.   switch (evt.eventId()) {
  379.   case MSG_COM_IN:
  380.     /**************************************************************************
  381.      * Blocks of incoming data are relayed here from the session object for
  382.      * processing.  (1) Display the data in the comm window.  The comm window
  383.      * works in terms of lines so we must break the data apart at line
  384.      * boundaries, and also know when the last line of the previous block
  385.      * is unfinished and needs to be continued.  (2) Build a line stream.
  386.      * Run the resulting complete lines through a filter which looks for
  387.      * challenges.
  388.      *************************************************************************/
  389.     {
  390.       char           buf[4000];
  391.       static IString line,prevline;
  392.       static Boolean continued = false;    // if last line not terminated
  393.       static Boolean filterNextPrompt = false; // cosmetics
  394.       int            linelen, peekc;
  395.  
  396.       while(1) {
  397.  
  398.     commStream.get(buf, sizeof(buf));
  399.     if (commStream.fail()) break;
  400.     line = IString(buf);
  401.     if ((peekc = commStream.peek()) == '\n')
  402.       commStream.get();
  403.  
  404.     // strip trailing CR (getline broke at LF of CRLF pair)
  405.     line.strip("\n\r");
  406. //    line.strip("\r");
  407.  
  408.     ITRACE_DEVELOP("MSG_COM_IN <"+line+">");
  409.  
  410.     /********************************************************
  411.      * if last line was not terminated, append this line to it
  412.      ********************************************************/
  413. //    cwin->setCursorAt(cwin->textLength());
  414.     if (continued) {
  415.       CommWindow::Cursor cur(*commWindow, CommWindow::Cursor::allItems);
  416.       cur.setToLast();
  417.       line = prevline + line;
  418.       ITRACE_DEVELOP("Continuing previous line: now <"+line+">");
  419.       continued = (commStream.peek() == EOF && peekc != '\n');
  420.       cancelBackspaces(line);
  421.       cwin->replaceAt(line, cur);
  422.     }
  423.     else
  424.       /**************************************************
  425.        * otherwise add this line as a new line at the end
  426.        **************************************************/
  427.       cwin->addAsLast(line);
  428.  
  429.     if (filterNextPrompt) {
  430.       // if hit, delete the line.  else, clear the bit if line is not
  431.       // a prefix of the prompt, meaning prompt did not follow the move
  432.       // string for some reason.
  433.       if (line == ICS_PROMPT) {
  434.         cwin->remove(cwin->count() - 1);
  435.         filterNextPrompt = false;
  436.       }
  437.       else if (IString(ICS_PROMPT).indexOf(line) != 1)
  438.         filterNextPrompt = false;
  439.     }
  440.  
  441.     /**************************************************************
  442.      * predict if this line is continued, and if not, run through
  443.      * move capture filter
  444.      **************************************************************/
  445.     if (! ((commStream.peek() == EOF && peekc != '\n'))) {
  446.       if (cwin->commFilter->filter(line)) {
  447.         cwin->remove(cwin->count() - 1);
  448.         filterNextPrompt = true;
  449.       }
  450.       else
  451.         cwin->lookForChallenge(line);
  452.     }
  453.     // could add a hook here to login to a dialup system
  454.     //icshook(line);
  455.     prevline = line;
  456.       }
  457.       continued = (commStream.peek() == EOF && peekc != '\n');
  458.       
  459.       /***************************************************************
  460.        * manage number of lines in listbox and the cursor position 
  461.        ***************************************************************/
  462.       if (cwin->count() > 300) {
  463.     // first get down to 300 for sure
  464.     while (cwin->remove(0) > 300) ;
  465.     // now delete 10% more (to prevent thrashing on continuous input)
  466.     for (int i=0; i<30; i++)
  467.       cwin->remove(0);
  468.       }
  469.  
  470.       /**************************************************************
  471.        * scroll listbox to bottom by setting 'top' to the last line
  472.        **************************************************************/
  473.       cwin->setTop(cwin->count());
  474.  
  475.       evt.setResult(0);
  476.       return true;
  477.     }
  478.     break;
  479.  
  480.   case MSG_COM_FONT:
  481.     ITRACE_DEVELOP("comm window: font message has arrived");
  482.     cwin->fontDialog();
  483.     return true;
  484.   }
  485.   return false;
  486. }
  487.  
  488.  
  489.  
  490. /**************************************************************************
  491.  * FUNCTION:    filter (class ACommFilterStyle12)
  492.  *
  493.  * DESCRIPTION: parse style 12 move messages from ICS
  494.  *   This notifies us that a move has been made in the game, and new
  495.  *   status information is available (clocks, etc).  The information
  496.  *   is digested and posted to the stat and board windows for display.
  497.  *
  498.  *   ics% help style12
  499.  *   A new style 12 now exists.  It has all the advantages of style 8 and
  500.  *   style 10.  Furthermore, style 12 inhibits the messages "OBSERVATION
  501.  *   REPORT" and "Game 7 (Quimbee vs.  Darooha)".  (Actually, I may make
  502.  *   this a feature of terse mode.  In any event, don't depend on those
  503.  *   messages.)
  504.  *   
  505.  *   The data is all on one line:  Here is an example:
  506.  *   
  507.  *   <12> rnbqkb-r pppppppp -----n-- -------- ----P--- -------- PPPPKPPP RNBQ-BNR B -
  508.  *   1 0 0 1 1 0 7 Quimbee Darooha 1 2 12 39 39 119 122 2 K/e1-e2 (0:06) Ke2
  509.  *   
  510.  *   This always begins on a new line, and there are always exactly 30
  511.  *   non-empty fields separated by blanks. They are:
  512.  *   
  513.  *   - The string "<12>" to identify this line.
  514.  *   - eight fields representing the board position.  The first one is file 8,
  515.  *     then file 7, etc, regardless of who's move it is.
  516.  *   - color whose turn it is to move ("B" or "W")
  517.  *   - -1 if the previous move was NOT a double pawn push,
  518.  *     otherwise the file (numbered 0--7 for a--h) in which
  519.  *     the double push was made
  520.  *   - can white still castle short? (0=no, 1=yes)
  521.  *   - can white still castle long?
  522.  *   - can black still castle short?
  523.  *   - can black still castle long?
  524.  *   - the number of moves made since the last irreversible move.
  525.  *     (0 if last move was irreversible.  If this is >= 100, the game
  526.  *     can be declared a draw due to the 50 move rule.)
  527.  *   - The game number
  528.  *   - White's name
  529.  *   - Black's name
  530.  *   - is it my turn to move in this game? (1=yes, -1=opponent's move, 
  531.  *     0=observing)
  532.  *   - initial time (in seconds) of the match
  533.  *   - increment of the match
  534.  *   - white strength
  535.  *   - black strength
  536.  *   - white's remaining time
  537.  *   - black's remaining time
  538.  *   - the number of the move about to be made
  539.  *     (standard chess numbering -- White's and Black's first moves
  540.  *     are both 1, etc.)
  541.  *   - verbose coordinate notation for the previous move ("none" if 
  542.  *     there were none)
  543.  *   - time taken to make previous move "(min:sec)".
  544.  *   - pretty notation for the previous move ("none" if there is none)
  545.  *   
  546.  *   New fields may be added to the end in the future, so programs should
  547.  *   parse from left to right.
  548.  **************************************************************************/
  549. Boolean ACommFilterStyle12::filter(IString &line)
  550. {
  551.   int gameNum, moveNum, timeW, timeB, lastMoveTime, clockRun;
  552.   char playerW[17], playerB[17];
  553.   IString  moveAlg, engMove;
  554.  
  555.   struct statData {
  556.     char    onMove;
  557.     int     wClock, bClock, gameNum;
  558.     char    wName[32], bName[32], move[32];
  559.   } *p;
  560.  
  561.   char       *pfld[30], *token;
  562.   int        min, sec, whosemove;
  563.   enum       {S12_ID=0, S12_RANK1, S12_RANK2, S12_RANK3, S12_RANK4, S12_RANK5,
  564.         S12_RANK6, S12_RANK7, S12_RANK8, S12_ONMOVE, S12_ENPASSANT,
  565.         S12_W00, S12_W000, S12_B00, S12_B000, S12_DRAWCTR, S12_GAMENUM,
  566.         S12_WNAME, S12_BNAME, S12_WHOSEMOVE, S12_INITCLOCK, S12_INCR,
  567.         S12_WMATERIAL, S12_BMATERIAL, S12_WTIME, S12_BTIME,
  568.         S12_MOVENUM, S12_MOVERAW, S12_MOVETIME, S12_MOVEALG};
  569.   AGame     *currentGame;
  570.   static int  failCount = 0;
  571.  
  572.   IFUNCTRACE_DEVELOP();
  573.  
  574.   if (line.subString(1,4) != "<12>")
  575.     return false;
  576.  
  577.   ITRACE_DEVELOP(line);
  578.  
  579.   /************************************************************************
  580.    * tokenize line into blank-separated fields; verify they're all present
  581.    ************************************************************************/
  582.   token = strtok((char *)line, " ");
  583.   for (int i = 0; i < 30; i++) {
  584.     if (token == NULL) {
  585.       // this has been happening lately so we'll recover by issuing
  586.       // a refresh command.
  587.       ITRACE_DEVELOP("error in style12: line ends too soon i=" + IString(i));
  588.       if (++failCount < 3)
  589.     aSession->write("refresh\nrefresh\n");
  590.       return false;       // error!  recovery: ignore  (should warn user)
  591.     }
  592.     pfld[i] = token;
  593.     token = strtok((char *)NULL, " ");   // dont care about tokens after 30th
  594.   }
  595.  
  596.   sscanf(pfld[S12_GAMENUM], "%3d", &gameNum);
  597.   sscanf(pfld[S12_WNAME], "%16s", &playerW);
  598.   sscanf(pfld[S12_BNAME], "%16s", &playerB);
  599.   sscanf(pfld[S12_MOVENUM], "%3d", &moveNum);
  600.   sscanf(pfld[S12_WTIME], "%5d", &timeW);
  601.   sscanf(pfld[S12_BTIME], "%5d", &timeB);
  602.   sscanf(pfld[S12_MOVETIME], "(%d:%d)", &min, &sec);
  603.   sscanf(pfld[S12_WHOSEMOVE], "%d", &whosemove);
  604.   lastMoveTime = min*60+sec;
  605.   clockRun = TRUE;
  606.   moveAlg = IString(pfld[S12_MOVEALG]);
  607.  
  608.   /**********************************
  609.    * post stat window with new info 
  610.    **********************************/
  611.   p = new struct statData;
  612.   p->wClock = timeW;
  613.   p->bClock = timeB;
  614.   p->onMove = pfld[S12_ONMOVE][0];
  615.   strcpy(p->wName, playerW);
  616.   strcpy(p->bName, playerB);
  617.   p->gameNum = gameNum;
  618.   if (pfld[S12_ONMOVE][0] == 'B')
  619.     strcpy(p->move, IString(moveNum) + ". " + moveAlg);
  620.   else
  621.     strcpy(p->move, IString(moveNum-1) + "... " + moveAlg);
  622.  
  623.   hMain.postEvent(MSG_STAT_UPDATE, (void *)p);
  624.  
  625.   hMain.postEvent(pfld[S12_ONMOVE][0] == 'W' ?
  626.           MSG_STAT_WHITE_ONMOVE :
  627.           MSG_STAT_BLACK_ONMOVE,
  628.           (ULONG) whosemove);
  629.   
  630.   /*************************************************************************
  631.    * send moves of active game to engine
  632.    * reformat as coordinate notation with promotion piece at end, e.g. a7b8q
  633.    *************************************************************************/
  634.   engMove = IString(pfld[S12_MOVERAW]);
  635.   if (whosemove == 1) {
  636.     if (engMove[2] == '/') { // P/e2-e4=Q (castling is sent verbatim)
  637.       int   eqpos;
  638.       engMove.remove(1,2);
  639.       engMove.remove(3,1);
  640.       engMove.remove(5);
  641.       if (eqpos = moveAlg.indexOf('=')) { // promotion indicator
  642.     engMove += moveAlg.subString(eqpos+1,1).lowerCase();
  643.       }
  644.     }
  645.     engineComm->sendCommand(engMove);
  646.   }
  647.  
  648.   currentGame = AGame::current();
  649.   for (int rank=0; rank<8; rank++) {
  650.     for (int file=0; file<8; file++) {
  651.       currentGame->setPiece(file, rank, 
  652.                 AGame::toPiece(pfld[S12_RANK8 - rank][file]));
  653.     }
  654.   }
  655.  
  656.   /********************************************************
  657.    * if initial position, initialize some state objects
  658.    ********************************************************/
  659.   if (moveNum==1 && strcmp("W", pfld[S12_ONMOVE]) == 0) {
  660.     ITRACE_DEVELOP("New game starting.  whosemove=" + IString(whosemove));
  661.     if (whosemove == 1)        // my move so put white at bottom
  662.       hBoard.postEvent(MSG_BOARD_WHITE_AT_BOTTOM);
  663.     else if (whosemove == -1)     // my opponent's move so put black at bottom
  664.       hBoard.postEvent(MSG_BOARD_WHITE_AT_TOP);
  665.     else            // observing only.  put white at bottom.
  666.       hBoard.postEvent(MSG_BOARD_WHITE_AT_BOTTOM);
  667.  
  668.     engineComm->sendCommand("new");
  669.   }
  670.  
  671.   else
  672.     hBoard.postEvent(MSG_BOARD_UPDATE);
  673.  
  674.   failCount = 0;
  675.   return true;
  676. }
  677.  
  678.  
  679. static void cancelBackspaces (IString &s)
  680. {
  681.   int i;
  682.  
  683.   while ((i = s.indexOf('\b')) > 1) {
  684.     s.remove(i-1,2);        // cancel BS and previous char
  685.   }
  686. }
  687.  
  688.  
  689. void icshook (IString &s)
  690. {
  691.   static int next = 0;
  692.   static char *script[] = {
  693.     "help abort",
  694.     "help abuse",
  695.     "help abusers",
  696.     "help accept",
  697.     "help ACM",
  698.     "help addresses",
  699.     "help adjourn",
  700.     "help adjudicate",
  701.     "help admins",
  702.     "help alias",
  703.     "help allobservers",
  704.     "help assess",
  705.     "help atmosphere",
  706.     "help backward",
  707.     "help bell",
  708.     "help best",
  709.     "help bugs",
  710.     "help channel",
  711.     "help clearmessages",
  712.     "help cls",
  713.     "help computers",
  714.     "help courtesyabort",
  715.     "help credits",
  716.     "help date",
  717.     "help decline",
  718.     "help definitions",
  719.     "help doah",
  720.     "help draw",
  721.     "help eco",
  722.     "help examine",
  723.     "help exit",
  724.     "help filters",
  725.     "help finger",
  726.     "help flag",
  727.     "help flip",
  728.     "help formula",
  729.     "help fortune",
  730.     "help forward",
  731.     "help ftp-hints",
  732.     "help games",
  733.     "help GIICS",
  734.     "help GMs",
  735.     "help gnusurf",
  736.     "help goodpasswd",
  737.     "help handle",
  738.     "help help",
  739.     "help history",
  740.     "help i",
  741.     "help ICS",
  742.     "help ICS-InTheNews",
  743.     "help inchannel",
  744.     "help index",
  745.     "help InetChessLibrary",
  746.     "help interfaces",
  747.     "help intro1",
  748.     "help intro10",
  749.     "help intro11",
  750.     "help intro2",
  751.     "help intro3",
  752.     "help intro4",
  753.     "help intro5",
  754.     "help intro6",
  755.     "help intro7",
  756.     "help intro8",
  757.     "help intro9",
  758.     "help Isbjoern",
  759.     "help kibitz",
  760.     "help lag",
  761.     "help lagflag",
  762.     "help lhistory",
  763.     "help LinaresRoundup",
  764.     "help lists",
  765.     "help llogons",
  766.     "help logons",
  767.     "help mailhelp",
  768.     "help mailoldmoves",
  769.     "help mailstored",
  770.     "help match",
  771.     "help messages",
  772.     "help mexamine",
  773.     "help minus",
  774.     "help mood",
  775.     "help moretime",
  776.     "help motd",
  777.     "help moves",
  778.     "help muzzle",
  779.     "help new",
  780.     "help notation",
  781.     "help notify",
  782.     "help nuke",
  783.     "help observe",
  784.     "help odds-games",
  785.     "help oldmoves",
  786.     "help open",
  787.     "help password",
  788.     "help PCA-candidates",
  789.     "help pending",
  790.     "help people",
  791.     "help players",
  792.     "help plus",
  793.     "help programmers",
  794.     "help promote",
  795.     "help quit",
  796.     "help quota",
  797.     "help rank",
  798.     "help rated",
  799.     "help ratings",
  800.     "help records",
  801.     "help refresh",
  802.     "help refresh",
  803.     "help registration",
  804.     "help registration",
  805.     "help resign",
  806.     "help revert",
  807.     "help rules",
  808.     "help say",
  809.     "help server",
  810.     "help set",
  811.     "help shout",
  812.     "help smoves",
  813.     "help splay-tree",
  814.     "help sposition",
  815.     "help sshout",
  816.     "help statistics",
  817.     "help stored",
  818.     "help style",
  819.     "help style",
  820.     "help style10",
  821.     "help style12",
  822.     "help suggestions",
  823.     "help takeback",
  824.     "help team-games",
  825.     "help tell",
  826.     "help terse-mode",
  827.     "help time",
  828.     "help tournaments",
  829.     "help Tourny-Rishi",
  830.     "help TournyWC",
  831.     "help unexamine",
  832.     "help upstatistics",
  833.     "help vars",
  834.     "help vars",
  835.     "help whisper",
  836.     "help who",
  837.     "help who1",
  838.     "help who2",
  839.     "help wild",
  840.     "help wild10",
  841.     "help wild11",
  842.     "help wild12",
  843.     "help wild13",
  844.     "help wild14",
  845.     "help wild5",
  846.     "help wild6",
  847.     "help wild7",
  848.     "help wild8",
  849.     "help wild9",
  850.     "help xtell",
  851.     "help znotl",
  852.     ""};
  853.   if (next == -1) return;
  854.   if (script[next][0] == 0) {next = -1; return;}
  855.   if (s == "aics% ") {
  856.     aSession->write(IString(script[next]) + "\n");
  857.     next++;
  858.   }
  859.   return;
  860. }
  861.  
  862. /**************************************************************************
  863.  * FUNCTION:    constructor (class AMatchDialog)
  864.  *
  865.  * DESCRIPTION: 
  866.  *
  867.  **************************************************************************/
  868. AMatchDialog:: AMatchDialog(IWindow *parent,       // ctor for this class
  869.                 IWindow *owner,
  870.                 IWindow *commWin,
  871.                 IString cName,
  872.                 int     cIsRated,
  873.                 int     cWildType,
  874.                 int     cIsWhite,
  875.                 int     cIsBlack,
  876.                 IString cClock,
  877.                 IString cOddsClock) :
  878.                 IFrameWindow (DID_MATCH, parent, owner)
  879. {
  880.   handleEventsFor(this);    // set self as command event handler
  881.  
  882.   /**********************************************
  883.    * save comm window to restore focus by
  884.    * setFocus call when dialog is dismissed
  885.    *********************************************/
  886.   commWind = commWin;
  887.  
  888.   /*******************************************
  889.    * set opponent and time control text values
  890.    *******************************************/
  891.   clock = new IEntryField(DID_MATCH_CLOCK, this);
  892.   clock->setText(cClock);
  893.  
  894.   oddsClock = new IEntryField(DID_MATCH_ODDSCLOCK, this);
  895.   if (cOddsClock != "") {
  896.     oddsClock->setText(cOddsClock);
  897.   }
  898.  
  899.   opponent = new IStaticText(DID_MATCH_OPPONENT, this);
  900.   opponent->setText(cName);
  901.  
  902.   /*******************************************
  903.    * set rated, wild, colour buttons
  904.    *******************************************/
  905.   rated = new ICheckBox(DID_MATCH_RATED, this);
  906.   if (cIsRated)
  907.     rated->select();
  908.  
  909.   wild = new ICheckBox(DID_MATCH_WILD, this);
  910.   wildType = new IEntryField(DID_MATCH_WILDTYPE, this);
  911.   if (cWildType) {
  912.     wild->select();
  913.     wildType->setText(IString(cWildType));
  914.   }
  915.  
  916.   white = new IRadioButton(DID_MATCH_WHITE, this);
  917.   black = new IRadioButton(DID_MATCH_BLACK, this);
  918.   neutral = new IRadioButton(DID_MATCH_NEUTRAL, this);
  919.   if (cIsWhite)
  920.     white->select();
  921.   else if (cIsBlack)
  922.     black->select();
  923.   else
  924.     neutral->select();
  925.  
  926.   /******************************************************************
  927.    * attach a handler to process check/radio box selections
  928.    ******************************************************************/
  929.   selectHdr = new AMatchSelectHandler();
  930.   selectHdr->handleEventsFor(this);
  931.  
  932.   /******************************************************************
  933.    * save initial parameters: if final parameters are the same,
  934.    * issue Accept instead of Match, because ics may reject a Match
  935.    * on the basis of Formulae.
  936.    ******************************************************************/
  937.   initialClock = cClock;
  938.   initialOddsClock = cOddsClock;
  939.   initialRated = cIsRated;
  940.   initialWhite = cIsWhite;
  941.   initialBlack = cIsBlack;
  942.   initialWildType = cWildType;
  943.   show();
  944. }
  945.  
  946. /**************************************************************************
  947.  * FUNCTION:    command (class AMatchDialog)
  948.  *
  949.  * DESCRIPTION: event handler for dialog Accept/Ignore/Decline pushbuttons
  950.  *
  951.  **************************************************************************/
  952. Boolean AMatchDialog:: command(ICommandEvent &cmdevt)
  953. {
  954.   IFUNCTRACE_DEVELOP();
  955.   switch(cmdevt.commandId()) {
  956.   case  DID_MATCH_ACCEPT:
  957.     {
  958.       IString  s;
  959.       ITRACE_DEVELOP("match accepted");
  960.       /******************************************************************
  961.        * figure out if any parameters changed.  if identical to the
  962.        * original challenge issue "accept", else issue "match" with
  963.        * all parameters specified explicitly.  this distinction is
  964.        * because the opponent can issue a challenge that does not
  965.        * satisfy his own formula (e.g. he issues a rated challenge
  966.        * and his formula says unrated only) and if we echo back the match
  967.        * command ics will reject it.
  968.        ******************************************************************/
  969.       if (clock->text() == initialClock &&
  970.       oddsClock->text() == initialOddsClock &&
  971.  
  972.       wild->isSelected() == (initialWildType != 0) &&
  973.       wildType->text() == IString(initialWildType) &&
  974.  
  975.       rated->isSelected() == initialRated &&
  976.       
  977.       white->isSelected() == initialWhite &&
  978.       black->isSelected() == initialBlack) {
  979.     s = "\naccept "+ opponent->text().word(1) + "\n";
  980.       }
  981.       else {
  982. //match <name> <time> <increment> <time2> <increment2> <r> <u> <wk> <color>
  983.     s = "\nmatch "+ opponent->text().word(1)+" "+ clock->text()+" ";
  984.     if (oddsClock->text() != IString(""))  
  985.       s += oddsClock->text()+" ";
  986.     s += (rated->isSelected()) ? "r " : "u ";
  987.     if (wild->isSelected() && wildType->text() != IString(""))   
  988.       s += "w"+wildType->text()+" ";
  989.     if (white->isSelected())  
  990.       s += "white ";
  991.     if (black->isSelected())  
  992.       s += "black ";
  993.     s += "\n";
  994.       }
  995.       aSession->write(s);
  996.       dismiss();
  997.       commWind->setFocus();
  998.       break;
  999.     }
  1000.   case  DID_MATCH_IGNORE:
  1001.     ITRACE_DEVELOP("match ignored");
  1002.     dismiss();
  1003.     commWind->setFocus();
  1004.     break;
  1005.   case  DID_MATCH_DECLINE:
  1006.     ITRACE_DEVELOP("match declined");
  1007.     aSession->write(IString("\ndecline ")+opponent->text().word(1)+"\n");
  1008.     dismiss();
  1009.     commWind->setFocus();
  1010.     break;
  1011.   }
  1012.   return false;            // pass to default processing
  1013. }
  1014.  
  1015.  
  1016. /**************************************************************************
  1017.  * FUNCTION:    command (class AMatchSelectHandler)
  1018.  *
  1019.  * DESCRIPTION: event handler for dialog radiobuttons and checkboxes
  1020.  *
  1021.  **************************************************************************/
  1022. Boolean AMatchSelectHandler:: selected(IControlEvent &evt)
  1023. {
  1024.   AMatchDialog   *dlg = (AMatchDialog *)evt.window();
  1025.  
  1026.   IFUNCTRACE_DEVELOP();
  1027.   switch(evt.controlId()) {
  1028.   case  DID_MATCH_RATED:
  1029.     ITRACE_DEVELOP("match rated checked");
  1030.     break;
  1031.   case  DID_MATCH_WILD:
  1032.     ITRACE_DEVELOP("match wild checked");
  1033.     break;
  1034.   case DID_MATCH_WHITE:
  1035.     ITRACE_DEVELOP("match white checked");
  1036.     dlg->black->deselect();
  1037.     dlg->neutral->deselect();
  1038.     break;
  1039.   case DID_MATCH_BLACK:
  1040.     ITRACE_DEVELOP("match black checked");
  1041.     dlg->white->deselect();
  1042.     dlg->neutral->deselect();
  1043.     break;
  1044.   case DID_MATCH_NEUTRAL:
  1045.     ITRACE_DEVELOP("match neutral checked");
  1046.     dlg->white->deselect();
  1047.     dlg->black->deselect();
  1048.     break;
  1049.   }
  1050.   return false;            // pass to default processing
  1051. }
  1052.