1 /*
2  * gnubg.c
3  *
4  * by Gary Wong <gtw@gnu.org>, 1998, 1999, 2000, 2001, 2002, 2003.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of version 3 or later of the GNU General Public License as
8  * published by the Free Software Foundation.
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * $Id: gnubg.c,v 1.998 2018/06/16 15:24:05 plm Exp $
20  */
21 
22 #include "config.h"
23 
24 #include "gnubgmodule.h"
25 #include "glib-ext.h"
26 
27 #include <sys/types.h>
28 #include <stdlib.h>
29 #if defined(HAVE_UNISTD_H)
30 #include <unistd.h>
31 #endif
32 #include <errno.h>
33 #include <stdio.h>
34 #include <glib.h>
35 #include <glib/gstdio.h>
36 #include <signal.h>
37 #include <ctype.h>
38 #include <locale.h>
39 #ifdef WIN32
40 #include <direct.h>
41 #include <io.h>
42 #endif
43 
44 #if defined(HAVE_LIB_READLINE)
45 static char *gnubg_histfile;
46 #include <readline/history.h>
47 #include <readline/readline.h>
48 static int fReadingOther;
49 static char szCommandSeparators[] = " \t\n\r\v\f";
50 #endif
51 
52 #include "analysis.h"
53 #include "backgammon.h"
54 #include "dice.h"
55 #include "drawboard.h"
56 #include "eval.h"
57 #include "sgf.h"
58 #include "export.h"
59 #include "import.h"
60 #include "matchequity.h"
61 #include "matchid.h"
62 #include "positionid.h"
63 #include "render.h"
64 #include "renderprefs.h"
65 #include "rollout.h"
66 #include "sound.h"
67 #include "progress.h"
68 #include "osr.h"
69 #include "format.h"
70 #include "relational.h"
71 #include "credits.h"
72 #include "external.h"
73 #include "neuralnet.h"
74 #include "util.h"
75 
76 #if defined(LIBCURL_PROTOCOL_HTTPS)
77 #include <curl/curl.h>
78 #endif
79 
80 #if defined(HAVE_SOCKETS)
81 #if defined(HAVE_SYS_SOCKET_H)
82 #include <sys/socket.h>
83 #include <netinet/in.h>
84 #include <arpa/inet.h>
85 #include <netdb.h>
86 #include <sys/un.h>
87 #endif                          /* #if HAVE_SYS_SOCKET_H */
88 #ifdef WIN32
89 #include <winsock2.h>
90 #include <ws2tcpip.h>
91 #define inet_aton(ip,addr)  (addr)->s_addr = inet_addr(ip), 1
92 #define inet_pton(fam,ip,addr) (addr)->s_addr = inet_addr(ip), 1
93 #endif                          /* #ifdef WIN32 */
94 #endif                          /* #if HAVE_SOCKETS */
95 
96 #if defined(USE_GTK)
97 #include <gtk/gtk.h>
98 #include "gtkboard.h"
99 #include "gtkgame.h"
100 #include "gtkprefs.h"
101 #include "gtksplash.h"
102 #include "gtkchequer.h"
103 #include "gtkwindows.h"
104 #endif
105 
106 #if defined(USE_BOARD3D)
107 #include "fun3d.h"
108 #endif
109 #include "multithread.h"
110 #include "openurl.h"
111 
112 #if defined(MSDOS) || defined(__MSDOS__) || defined(WIN32)
113 #define NO_BACKSLASH_ESCAPES 1
114 #endif
115 
116 #if defined(USE_GTK)
117 int fX = FALSE;                 /* use X display */
118 unsigned int nDelay = 300;
119 int fNeedPrompt = FALSE;
120 #if defined(HAVE_LIB_READLINE)
121 int fReadingCommand;
122 #endif
123 #endif
124 
125 static int fNoRC = FALSE;
126 static char *autosave = NULL;
127 static int loading_rc = FALSE;
128 
129 const char *intro_string =
130     N_("This program comes with ABSOLUTELY NO WARRANTY; for details type `show warranty'.\n"
131        "This is free software, and you are welcome to redistribute it under certain conditions; type `show copying' for details.\n");
132 char *szLang = NULL;
133 
134 const char szDefaultPrompt[] = "(\\p) ", *szPrompt = szDefaultPrompt;
135 int fInteractive;
136 
137 matchstate ms = {
138     {{0}, {0}},                 /* anBoard */
139     {0},                        /* anDice */
140     -1,                         /* fTurn */
141     0,                          /* fResigned */
142     0,                          /* fResignationDeclined */
143     FALSE,                      /* fDoubled */
144     0,                          /* cGames */
145     -1,                         /* fMove */
146     -1,                         /* fCubeOwner */
147     FALSE,                      /* fCrawford */
148     FALSE,                      /* fPostCrawford */
149     0,                          /* nMatchTo */
150     {0, 0},                     /* anScore */
151     1,                          /* nCube */
152     0,                          /* cBeavers */
153     VARIATION_STANDARD,         /*bgv */
154     TRUE,                       /* fCubeUse */
155     TRUE,                       /* fJacoby */
156     GAME_NONE                   /* gs */
157 };
158 
159 ConstTanBoard
msBoard(void)160 msBoard(void)
161 {
162     return (ConstTanBoard) ms.anBoard;
163 }
164 
165 matchinfo mi;
166 
167 float rRatingOffset = 2050;
168 int fAnalyseCube = TRUE;
169 int fAnalyseDice = TRUE;
170 int fAnalyseMove = TRUE;
171 int fAutoBearoff = FALSE;
172 int fAutoCrawford = 1;
173 int fAutoGame = TRUE;
174 int fAutoMove = FALSE;
175 int fAutoRoll = TRUE;
176 int fCheat = FALSE;
177 int fConfirmNew = TRUE;
178 int fConfirmSave = TRUE;
179 int nAutoSaveTime = 15;
180 int fAutoSaveRollout = FALSE;
181 int fAutoSaveAnalysis = FALSE;
182 int fAutoSaveConfirmDelete = TRUE;
183 
184 /* FIXME: This is at best useless, at worst misleading, as a global flag.
185  * It should be deduced from the rollout context when needed.
186  * See for instance show.c:ShowRollout() around line 270 */
187 int fCubeEqualChequer = FALSE;
188 
189 int fCubeUse = TRUE;
190 int fDisplay = TRUE;
191 int fFullScreen = FALSE;
192 int fGotoFirstGame = FALSE;
193 int fInvertMET = FALSE;
194 int fJacoby = TRUE;
195 int fOutputRawboard = FALSE;
196 int fPlayersAreSame = TRUE;
197 int fRecord = TRUE;
198 int fShowProgress;
199 int fStyledGamelist = TRUE;
200 int fTruncEqualPlayer0 = TRUE;
201 int fTutorChequer = TRUE;
202 int fTutorCube = TRUE;
203 int fTutor = FALSE;
204 int fEvalSameAsAnalysis = FALSE;
205 int fJustSwappedPlayers = FALSE;
206 int nConfirmDefault = -1;
207 int nThreadPriority = 0;
208 int nToolbarStyle = 2;
209 unsigned int afCheatRoll[2] = { 0, 0 };
210 
211 unsigned int cAutoDoubles = 0;
212 unsigned int nBeavers = 3;
213 unsigned int nDefaultLength = 7;
214 
215 #if defined(USE_BOARD3D)
216 int fSync = -1;                 /* Not set */
217 int fResetSync = FALSE;         /* May need to wait for main window */
218 #endif
219 
220 skilltype TutorSkill = SKILL_DOUBTFUL;
221 int nTutorSkillCurrent = 0;
222 
223 char *szCurrentFileName = NULL;
224 char *szCurrentFolder = NULL;
225 
226 
227 int fNextTurn = FALSE, fComputing = FALSE;
228 
229 float rEvalsPerSec = -1.0f;
230 float arLuckLevel[] = {
231     0.6f,                       /* LUCK_VERYBAD */
232     0.3f,                       /* LUCK_BAD */
233     0,                          /* LUCK_NONE */
234     0.3f,                       /* LUCK_GOOD */
235     0.6f                        /* LUCK_VERYGOOD */
236 }, arSkillLevel[] = {
237     0.16f,                      /* SKILL_VERYBAD */
238     0.08f,                      /* SKILL_BAD */
239     0.04f,                      /* SKILL_DOUBTFUL */
240     0                           /* SKILL_NONE */
241 };
242 
243 #include "movefilters.inc"
244 
245 rngcontext *rngctxRollout = NULL;
246 
247 rolloutcontext rcRollout = {
248     {
249      /* player 0/1 cube decision */
250      {TRUE, 2, TRUE, TRUE, 0.0},
251      {TRUE, 2, TRUE, TRUE, 0.0}
252      },
253     {
254      /* player 0/1 chequerplay */
255      {TRUE, 0, TRUE, TRUE, 0.0},
256      {TRUE, 0, TRUE, TRUE, 0.0}
257      },
258 
259     {
260      /* player 0/1 late cube decision */
261      {TRUE, 2, TRUE, TRUE, 0.0},
262      {TRUE, 2, TRUE, TRUE, 0.0}
263      },
264     {
265      /* player 0/1 late chequerplay */
266      {TRUE, 0, TRUE, TRUE, 0.0},
267      {TRUE, 0, TRUE, TRUE, 0.0}
268      },
269     /* truncation point cube and chequerplay */
270     {TRUE, 2, TRUE, TRUE, 0.0},
271     {TRUE, 2, TRUE, TRUE, 0.0},
272 
273     /* move filters */
274     {MOVEFILTER_NORMAL, MOVEFILTER_NORMAL},
275     {MOVEFILTER_NORMAL, MOVEFILTER_NORMAL},
276 
277     TRUE,                       /* cubeful */
278     TRUE,                       /* variance reduction */
279     FALSE,                      /* initial position */
280     TRUE,                       /* rotate */
281     TRUE,                       /* truncate at BEAROFF2 for cubeless rollouts */
282     TRUE,                       /* truncate at BEAROFF2_OS for cubeless rollouts */
283     FALSE,                      /* late evaluations */
284     FALSE,                      /* Truncation enabled */
285     FALSE,                      /* no stop on STD */
286     FALSE,                      /* no stop on JSD */
287     FALSE,                      /* no move stop on JSD */
288     10,                         /* truncation */
289     1296,                       /* number of trials */
290     5,                          /* late evals start here */
291     RNG_MERSENNE,               /* RNG */
292     0,                          /* seed */
293     324,                        /* minimum games  */
294     0.01f,                      /* stop when std's are lower than 0.01 */
295     324,                        /* minimum games  */
296     2.33f,                      /* stop when best has j.s.d. for 99% confidence */
297     0,                          /* nGamesDone */
298     0.0,                        /* rStoppedOnJSD */
299     0,                          /* nSkip */
300 };
301 
302 /* parameters for `eval' and `hint' */
303 
304 #define EVALSETUP_WORLDCLASS  { \
305   /* evaltype */ \
306   EVAL_EVAL, \
307   /* evalcontext */ \
308   { TRUE, 2, TRUE, TRUE, 0.0 }, \
309   /* rolloutcontext */ \
310   { \
311     { \
312       { FALSE, 2, TRUE, TRUE, 0.0 }, /* player 0 cube decision */ \
313       { FALSE, 2, TRUE, TRUE, 0.0 } /* player 1 cube decision */ \
314     }, \
315     { \
316       { FALSE, 0, TRUE, TRUE, 0.0 }, /* player 0 chequerplay */ \
317       { FALSE, 0, TRUE, TRUE, 0.0 } /* player 1 chequerplay */ \
318     }, \
319     { \
320       { FALSE, 2, TRUE, TRUE, 0.0 }, /* p 0 late cube decision */ \
321       { FALSE, 2, TRUE, TRUE, 0.0 } /* p 1 late cube decision */ \
322     }, \
323     { \
324       { FALSE, 0, TRUE, TRUE, 0.0 }, /* p 0 late chequerplay */ \
325       { FALSE, 0, TRUE, TRUE, 0.0 } /* p 1 late chequerplay */ \
326     }, \
327     { FALSE, 2, TRUE, TRUE, 0.0 }, /* truncate cube decision */ \
328     { FALSE, 2, TRUE, TRUE, 0.0 }, /* truncate chequerplay */ \
329     { MOVEFILTER_NORMAL, MOVEFILTER_NORMAL }, \
330     { MOVEFILTER_NORMAL, MOVEFILTER_NORMAL }, \
331   FALSE, /* cubeful */ \
332   TRUE, /* variance reduction */ \
333   FALSE, /* initial position */ \
334   TRUE, /* rotate */ \
335   TRUE, /* truncate at BEAROFF2 for cubeless rollouts */ \
336   TRUE, /* truncate at BEAROFF2_OS for cubeless rollouts */ \
337   FALSE, /* late evaluations */ \
338   TRUE,  /* Truncation enabled */ \
339   FALSE,  /* no stop on STD */ \
340   FALSE,  /* no stop on JSD */ \
341   FALSE,  /* no move stop on JSD */ \
342   10, /* truncation */ \
343   1296, /* number of trials */ \
344   5,  /* late evals start here */ \
345   RNG_MERSENNE, /* RNG */ \
346   0,  /* seed */ \
347   324,    /* minimum games  */ \
348   0.01f,  /* stop when std's are lower than 0.01 */ \
349   324,    /* minimum games  */ \
350   2.33f,  /* stop when best has j.s.d. for 99% confidence */ \
351   0, \
352   0.0, \
353   0 \
354   } \
355 }
356 
357 /* parameters for analysis */
358 
359 #define EVALSETUP_SUPREMO  { \
360   /* evaltype */ \
361   EVAL_EVAL, \
362   /* evalcontext */ \
363   { TRUE, 2, TRUE, TRUE, 0.0 }, \
364   /* rolloutcontext */ \
365   { \
366     { \
367       { FALSE, 2, TRUE, TRUE, 0.0 }, /* player 0 cube decision */ \
368       { FALSE, 2, TRUE, TRUE, 0.0 } /* player 1 cube decision */ \
369     }, \
370     { \
371       { FALSE, 0, TRUE, TRUE, 0.0 }, /* player 0 chequerplay */ \
372       { FALSE, 0, TRUE, TRUE, 0.0 } /* player 1 chequerplay */ \
373     }, \
374     { \
375       { FALSE, 2, TRUE, TRUE, 0.0 }, /* p 0 late cube decision */ \
376       { FALSE, 2, TRUE, TRUE, 0.0 } /* p 1 late cube decision */ \
377     }, \
378     { \
379       { FALSE, 0, TRUE, TRUE, 0.0 }, /* p 0 late chequerplay */ \
380       { FALSE, 0, TRUE, TRUE, 0.0 } /* p 1 late chequerplay */ \
381     }, \
382     { FALSE, 2, TRUE, TRUE, 0.0 }, /* truncate cube decision */ \
383     { FALSE, 2, TRUE, TRUE, 0.0 }, /* truncate chequerplay */ \
384     { MOVEFILTER_NORMAL, MOVEFILTER_NORMAL }, \
385     { MOVEFILTER_NORMAL, MOVEFILTER_NORMAL }, \
386   FALSE, /* cubeful */ \
387   TRUE, /* variance reduction */ \
388   FALSE, /* initial position */ \
389   TRUE, /* rotate */ \
390   TRUE, /* truncate at BEAROFF2 for cubeless rollouts */ \
391   TRUE, /* truncate at BEAROFF2_OS for cubeless rollouts */ \
392   FALSE, /* late evaluations */ \
393   TRUE,  /* Truncation enabled */ \
394   FALSE,  /* no stop on STD */ \
395   FALSE,  /* no stop on JSD */ \
396   FALSE,  /* no move stop on JSD */ \
397   10, /* truncation */ \
398   1296, /* number of trials */ \
399   5,  /* late evals start here */ \
400   RNG_MERSENNE, /* RNG */ \
401   0,  /* seed */ \
402   324,    /* minimum games  */ \
403   0.01f,  /* stop when std's are lower than 0.01 */ \
404   324,    /* minimum games  */ \
405   2.33f,  /* stop when best has j.s.d. for 99% confidence */ \
406   0, \
407   0.0, \
408   0 \
409   } \
410 }
411 
412 evalsetup esEvalChequer = EVALSETUP_WORLDCLASS;
413 evalsetup esEvalCube = EVALSETUP_WORLDCLASS;
414 evalsetup esAnalysisChequer = EVALSETUP_SUPREMO;
415 evalsetup esAnalysisCube = EVALSETUP_SUPREMO;
416 
417 movefilter aamfEval[MAX_FILTER_PLIES][MAX_FILTER_PLIES] = MOVEFILTER_NORMAL;
418 movefilter aamfAnalysis[MAX_FILTER_PLIES][MAX_FILTER_PLIES] = MOVEFILTER_LARGE;
419 
420 extern evalsetup *
GetEvalChequer(void)421 GetEvalChequer(void)
422 {
423     return fEvalSameAsAnalysis ? &esAnalysisChequer : &esEvalChequer;
424 }
425 
426 extern evalsetup *
GetEvalCube(void)427 GetEvalCube(void)
428 {
429     return fEvalSameAsAnalysis ? &esAnalysisCube : &esEvalCube;
430 }
431 
432 extern TmoveFilter *
GetEvalMoveFilter(void)433 GetEvalMoveFilter(void)
434 {
435     return fEvalSameAsAnalysis ? &aamfAnalysis : &aamfEval;
436 }
437 
438 exportsetup exsExport = {
439     TRUE,                       /* include annotations */
440     TRUE,                       /* include analysis */
441     TRUE,                       /* include statistics */
442     TRUE,                       /* include match information */
443 
444     1,                          /* display board for all moves */
445     -1,                         /* both players */
446 
447     5,                          /* display max 5 moves */
448     TRUE,                       /* show detailed probabilities */
449     /* do not show move parameters */
450     {FALSE, TRUE}
451     ,
452     /* display all moves */
453     {TRUE, TRUE, TRUE, TRUE}
454     ,
455 
456     TRUE,                       /* show detailed prob. for cube decisions */
457     {FALSE, TRUE}
458     ,                           /* do not show move parameters */
459     /* display all cube decisions */
460     {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE}
461     ,
462 
463     NULL,                       /* HTML URL to pictures */
464     HTML_EXPORT_TYPE_GNU,
465     NULL,                       /* HTML extension */
466     HTML_EXPORT_CSS_HEAD,       /* write CSS stylesheet in <head> */
467 
468     4,                          /* PNG size */
469     4                           /* HTML size */
470 };
471 
472 
473 player ap[2] = {
474     {"gnubg", PLAYER_GNU, EVALSETUP_WORLDCLASS, EVALSETUP_WORLDCLASS, MOVEFILTER_NORMAL, 0, NULL}
475     ,
476     {"user", PLAYER_HUMAN, EVALSETUP_WORLDCLASS, EVALSETUP_WORLDCLASS, MOVEFILTER_NORMAL, 0, NULL}
477 };
478 
479 char default_names[2][31] = { "gnubg", "user" };
480 
481 char player1aliases[256] = "";
482 
483 /* Usage strings */
484 static char szDICE[] = N_("<die> <die>"),
485     szCOMMAND[] = N_("<command>"),
486     szCOMMENT[] = N_("<comment>"),
487     szER[] = "evaluation|rollout",
488     szFILENAME[] = N_("<filename>"),
489     szKEYVALUE[] = N_("[<key>=<value> ...]"),
490     szLENGTH[] = N_("<length>"),
491     szLIMIT[] = N_("<limit>"),
492     szMILLISECONDS[] = N_("<milliseconds>"),
493     szMOVE[] = N_("<from> <to> ..."),
494     szFILTER[] = N_("<ply> <num.xjoin to accept (0 = skip)> "
495                     "[<num. of extra moves to accept> <tolerance>]"),
496     szNAME[] = N_("<name>"),
497     szQUIET[] = "[quiet]",
498     szLANG[] = N_("system|<language code>"),
499     szONOFF[] = "on|off",
500     szOPTCOMMAND[] = N_("[command]"),
501     szOPTDATE[] = N_("[yyyy-mm-dd]"),
502     szOPTDEPTH[] = N_("[depth]"),
503     szOPTFILENAME[] = N_("[filename]"),
504     szOPTLENGTH[] = N_("[length]"),
505     szOPTMODULUSOPTSEED[] = N_("[modulus <modulus>|factors <factor> <factor>] "
506                                "[seed]"),
507     szOPTNAME[] = N_("[name]"),
508     szOPTPOSITION[] = N_("[position]"),
509     szOPTSEED[] = N_("[seed]"),
510     szOPTVALUE[] = N_("[value]"),
511     szPLAYER[] = N_("<player>"),
512     szPLAYEROPTRATING[] = N_("<player> [rating]"),
513     szPLIES[] = N_("<plies>"),
514     szPOSITION[] = N_("<position>"),
515     szPRIORITY[] = N_("<priority>"),
516     szPROMPT[] = N_("<prompt>"),
517     szSCORE[] = N_("<score> [length]"),
518     szSIZE[] = N_("<size>"),
519     szSTEP[] = N_("[game|roll|rolled|marked] <count>"),
520     szTRIALS[] = N_("<trials>"),
521     szVALUE[] = N_("<value>"),
522     szMATCHID[] = N_("<matchid>"),
523     szGNUBGID[] = N_("<gnubgid>"),
524     szXGID[] = N_("<xgid>"),
525     szURL[] = "<URL>",
526     szMAXERR[] = N_("<fraction>"), szMINGAMES[] = N_("<minimum games to rollout>"), szFOLDER[] = N_("<folder>"),
527 #if defined(USE_GTK)
528     szWARN[] = N_("[<warning>]"), szWARNYN[] = N_("<warning> on|off"),
529 #endif
530     szJSDS[] = N_("<joint standard deviations>"), szSTDDEV[] = N_("<std dev>");
531 
532 /* Command defines moved into separate file */
533 #include "commands.inc"
534 
535 static int iProgressMax, iProgressValue, fInProgress;
536 static char *pcProgress;
537 static psighandler shInterruptOld;
538 char *default_import_folder = NULL;
539 char *default_export_folder = NULL;
540 char *default_sgf_folder = NULL;
541 
542 const char *szHomeDirectory;
543 
544 static char const *aszBuildInfo[] = {
545 #if defined(USE_PYTHON)
546 #if (PY_MAJOR_VERSION == 2)
547     N_("Python 2 supported."),
548 #elif (PY_MAJOR_VERSION == 3)
549     N_("Python 3 supported."),
550 #else
551     #error "Unsupported python version"
552 #endif
553 #endif
554 #if defined(USE_SQLITE)
555     N_("SQLite database supported."),
556 #endif
557 #if defined(USE_GTK)
558 #if GTK_CHECK_VERSION(3,0,0)
559     N_("GTK3 graphical interface supported."),
560 #else
561     N_("GTK2 graphical interface supported."),
562 #endif
563 #endif
564 #if defined(HAVE_SOCKETS)
565     N_("External players supported."),
566 #endif
567 #if defined(HAVE_LIBGMP)
568     N_("Long RNG seeds supported."),
569 #endif
570 #if defined(USE_BOARD3D)
571     N_("3D boards supported."),
572 #endif
573 #if defined(WIN32)
574     N_("Windows sound system supported."),
575 #elif defined(HAVE_APPLE_COREAUDIO)
576     N_("Apple CoreAudio sound system supported."),
577 #elif defined(HAVE_APPLE_QUICKTIME)
578     N_("Apple QuickTime sound system supported."),
579 #elif defined(HAVE_CANBERRA)
580     N_("libcanberra sound system supported."),
581 #endif
582 #if defined(USE_MULTITHREAD)
583     N_("Multiple threads supported."),
584 #endif
585 #if defined(USE_SIMD_INSTRUCTIONS)
586 #if defined(USE_SSE2)
587     N_("SSE/SSE2 supported."),
588 #elif defined(USE_AVX)
589     N_("AVX supported."),
590 #elif defined(USE_SSE)
591     N_("SSE supported."),
592 #else
593     N_("NEON supported."),
594 #endif
595 #endif
596     NULL
597 };
598 
599 extern const char *
GetBuildInfoString(void)600 GetBuildInfoString(void)
601 {
602     static const char **ppch = aszBuildInfo;
603 
604     if (!*ppch) {
605         ppch = aszBuildInfo;
606         return NULL;
607     }
608     return *ppch++;
609 }
610 
611 /*
612  * general token extraction
613  input: ppch pointer to pointer to command
614  szToekns - string of token separators
615  output: NULL if no token found
616  ptr to extracted token if found. Token is in original location
617  in input string, but null terminated if not quoted, token
618  will have been moved forward over quote character when quoted
619  ie:
620  input:  '  abcd efgh'
621  output  '  abcd\0efgh'
622  return value points to abcd, ppch points to efgh
623  input   '  "jklm" nopq'
624  output  ;  jklm\0 nopq'
625  return value points to jklm, ppch points to space before
626  the 'n'
627  ppch points past null terminator
628 
629  ignores leading whitespace, advances ppch over token and trailing
630  whitespace
631 
632  matching single or double quotes are allowed, any character outside
633  of quotes or in doubly quoted strings can be escaped with a
634  backslash and will be taken as literal.  Backslashes within single
635  quoted strings are taken literally. Multiple quoted strings can be
636  concatenated.
637 
638  For example: input ' abc\"d"e f\"g h i"jk'l m n \" o p q'rst uvwzyz'
639  with the terminator list ' \t\r\n\v\f'
640  The returned token will be the string
641  <abc"de f"g h j ijkl m n \" o p qrst>
642  ppch will point to the 'u'
643  The \" between c and d is not in a single quoted string, so is reduced to
644  a double quote and is *not* the start of a quoted string.
645  The " before the 'd' begins a double quoted string, so spaces and tabs are
646  not terminators. The \" between f and g is reduced to a double quote and
647  does not terminate the quoted string. which ends with the double quote
648  between i and j. The \" between n and o is taken as a pair of literal
649  characters because they are within the single quoted string beginning
650  before l and ending after q.
651  It is not possible to put a single quote within a single quoted string.
652  You can have single quotes unescaped within double quoted strings and
653  double quotes unescaped within single quoted strings.
654  */
655 extern char *
NextTokenGeneral(char ** ppch,const char * szTokens)656 NextTokenGeneral(char **ppch, const char *szTokens)
657 {
658 
659 
660     char *pch, *pchSave, chQuote = 0;
661     int fEnd = FALSE;
662 #if !defined(G_DISABLE_ASSERT)
663     char *pchEnd;
664 #endif
665 
666     if (!*ppch)
667         return NULL;
668 
669 #if !defined(G_DISABLE_ASSERT)
670     pchEnd = strchr(*ppch, 0);
671 #endif
672 
673     /* skip leading whitespace */
674     while (isspace(**ppch))
675         (*ppch)++;
676 
677     if (!*(pch = pchSave = *ppch))
678         /* nothing left */
679         return NULL;
680 
681     while (!fEnd) {
682 
683         if (**ppch && strchr(szTokens, **ppch)) {
684             /* this character ends token */
685             if (!chQuote) {
686                 fEnd = TRUE;
687                 (*ppch)++;      /* step over token */
688             } else
689                 *pchSave++ = **ppch;
690         } else {
691             switch (**ppch) {
692             case '"':
693                 /* quote mark */
694                 if (!chQuote)
695                     /* start quoting */
696                     chQuote = **ppch;
697                 else if (chQuote == **ppch)
698                     /* end quoting */
699                     chQuote = 0;
700                 else
701                     /* literal */
702                     *pchSave++ = **ppch;
703                 break;
704 
705 #ifdef NO_BACKSLASH_ESCAPES
706             case '%':
707 #else
708             case '\\':
709 #endif
710                 /* backslash */
711                 if (chQuote == '\'')
712                     /* literal */
713                     *pchSave++ = **ppch;
714                 else {
715                     (*ppch)++;
716 
717                     if (**ppch)
718                         /* next character is literal */
719                         *pchSave++ = **ppch;
720                     else {
721                         /* end of string -- the backlash doesn't quote anything */
722 #ifdef NO_BACKSLASH_ESCAPES
723                         *pchSave++ = '%';
724 #else
725                         *pchSave++ = '\\';
726 #endif
727                         fEnd = TRUE;
728                     }
729                 }
730                 break;
731 
732             case 0:
733                 /* end of string -- always ends token */
734                 fEnd = TRUE;
735                 break;
736 
737             default:
738                 *pchSave++ = **ppch;
739             }
740 
741         }
742 
743         if (!fEnd)
744             (*ppch)++;
745     }
746 
747     while (isspace(**ppch))
748         (*ppch)++;
749 
750     *pchSave = 0;
751 
752 #if !defined(G_DISABLE_ASSERT)
753     g_assert(pchSave <= pchEnd);
754     g_assert(*ppch <= pchEnd);
755     g_assert(pch <= pchEnd);
756 #endif
757 
758     return pch;
759 
760 }
761 
762 /* extrace a token from a string. Tokens are terminated by tab, newline,
763  * carriage return, vertical tab or form feed.
764  * Input:
765  *
766  * ppch = pointer to pointer to input string. This will be updated
767  * to point past any token found. If the string is exhausetd,
768  * the pointer at ppch will point to the terminating NULL, so it is
769  * safe to call this function repeatedly after failure
770  *
771  * Output:
772  * null terminated token if found or NULL if no tokens present.
773  */
774 extern char *
NextToken(char ** ppch)775 NextToken(char **ppch)
776 {
777 
778     return NextTokenGeneral(ppch, " \t\n\r\v\f");
779 
780 }
781 
782 /* return a count of the number of separate runs of one or more
783  * non-whitespace characters. This is the number of tokens that
784  * NextToken() will return. It may not be the number of tokens
785  * NextTokenGeneral() will return, as it does not count quoted strings
786  * containing whitespace as single tokens
787  */
788 static int
CountTokens(char * pch)789 CountTokens(char *pch)
790 {
791 
792     int c = 0;
793 
794     do {
795         while (isspace(*pch))
796             pch++;
797 
798         if (*pch) {
799             c++;
800 
801             while (*pch && !isspace(*pch))
802                 pch++;
803         }
804     } while (*pch);
805 
806     return c;
807 }
808 
809 /* extract a token and convert to double. On error or no token, return
810  * ERR_VAL (a very large negative double.
811  */
812 
813 extern float
ParseReal(char ** ppch)814 ParseReal(char **ppch)
815 {
816 
817     char *pch, *pchOrig;
818     float r;
819 
820     if (!ppch || !(pchOrig = NextToken(ppch)))
821         return ERR_VAL;
822 
823     r = (float) g_ascii_strtod(pchOrig, &pch);
824 
825     return *pch ? ERR_VAL : r;
826 }
827 
828 /* get the next token from the input and convert as an
829  * integer. Returns INT_MIN on empty input or non-numerics found. Does
830  * handle negative integers. On failure, one token (if any were available
831  * will have been consumed, it is not pushed back into the input.
832  */
833 extern int
ParseNumber(char ** ppch)834 ParseNumber(char **ppch)
835 {
836 
837     char *pch, *pchOrig;
838 
839     if (!ppch || !(pchOrig = NextToken(ppch)))
840         return INT_MIN;
841 
842     for (pch = pchOrig; *pch; pch++)
843         if (!isdigit(*pch) && *pch != '-')
844             return INT_MIN;
845 
846     return atoi(pchOrig);
847 }
848 
849 /* get a player either by name or as player 0 or 1 (indicated by the single
850  * character '0' or '1'. Returns -1 on no input, 2 if not a recoginsed name
851  * Note - this is not a token extracting routine, it expects to be handed
852  * an already extracted token
853  */
854 extern int
ParsePlayer(char * sz)855 ParsePlayer(char *sz)
856 {
857 
858     int i;
859 
860     if (!sz)
861         return -1;
862 
863     if ((*sz == '0' || *sz == '1') && !sz[1])
864         return *sz - '0';
865 
866     for (i = 0; i < 2; i++)
867         if (!CompareNames(sz, ap[i].szName))
868             return i;
869 
870     if (!StrNCaseCmp(sz, "both", strlen(sz)))
871         return 2;
872 
873     return -1;
874 }
875 
876 
877 /* Convert a string to a board array.  Currently allows the string to
878  * be a position ID, a gnubg-nn position string, "=n" notation,
879  * or empty (in which case the current board is used).
880  *
881  * The input string should be specified in *ppch; this string must be
882  * modifiable, and the pointer will be updated to point to the token
883  * following a board specification if possible (see NextToken()).  The
884  * board will be returned in an, and if pchDesc is non-NULL, then
885  * descriptive text (the position ID, formatted move, or "Current
886  * position", depending on the input) will be stored there.
887  *
888  * Returns -1 on failure, 0 on success, or 1 on success if the position
889  * specified has the opponent on roll (e.g. because it used "=n" notation). */
890 extern int
ParsePosition(TanBoard an,char ** ppch,char * pchDesc)891 ParsePosition(TanBoard an, char **ppch, char *pchDesc)
892 {
893     int i;
894     char *pch;
895 
896     /* FIXME allow more formats, e.g. FIBS "boardstyle 3" */
897 
898     if (!ppch || !(pch = NextToken(ppch))) {
899         memcpy(an, msBoard(), sizeof(TanBoard));
900 
901         if (pchDesc)
902             strcpy(pchDesc, _("Current position"));
903 
904         return 0;
905     }
906 
907     if (!strcmp(pch, "simple")) {
908 
909         /* board given as 26 integers.
910          * integer 1   : # of my chequers on the bar
911          * integer 2-25: number of chequers on point 1-24
912          *               positive ints: my chequers
913          *               negative ints: opp chequers
914          * integer 26  : # of opp chequers on the bar
915          */
916 
917         int n;
918 
919         for (i = 0; i < 26; i++) {
920 
921             if ((n = ParseNumber(ppch)) == INT_MIN) {
922                 outputf(_("`simple' must be followed by 26 integers; " "found only %d\n"), i);
923                 return -1;
924             }
925 
926             if (i == 0) {
927                 /* my chequers on the bar */
928                 an[1][24] = abs(n);
929             } else if (i == 25) {
930                 /* opp chequers on the bar */
931                 an[0][24] = abs(n);
932             } else {
933 
934                 an[1][i - 1] = 0;
935                 an[0][24 - i] = 0;
936 
937                 if (n < 0)
938                     an[0][24 - i] = -n;
939                 else if (n > 0)
940                     an[1][i - 1] = n;
941 
942             }
943 
944         }
945 
946         if (pchDesc)
947             strcpy(pchDesc, *ppch);
948 
949         *ppch = NULL;
950 
951         return CheckPosition((ConstTanBoard) an) ? 0 : -1;
952     }
953 
954     if (strlen(pch) == 20) {    /* gnubg-nn position string */
955         static oldpositionkey key;
956 
957         for (i = 0; i < 10; ++i) {
958             if (pch[2 * i + 0] >= 'A' && pch[2 * i + 0] <= 'P' && pch[2 * i + 1] >= 'A' && pch[2 * i + 1] <= 'P')
959                 key.auch[i] = (unsigned char) (((pch[2 * i + 0] - 'A') << 4)
960                                                + (pch[2 * i + 1] - 'A'));
961             else {
962                 outputl(_("Illegal position."));
963                 return -1;
964             }
965         }
966 
967         oldPositionFromKey(an, &key);
968 
969         return 0;
970     }
971 
972     if (!PositionFromID(an, pch)) {
973         outputl(_("Illegal position."));
974         return -1;
975     }
976 
977     if (pchDesc)
978         strcpy(pchDesc, pch);
979 
980     return 0;
981 }
982 
983 /* Parse "key=value" pairs on a command line.  PPCH takes a pointer to
984  * a command line on input, and returns a pointer to the next parameter.
985  * The key is returned in apch[ 0 ], and the value in apch[ 1 ].
986  * The function return value is the number of parts successfully read
987  * (0 = no key was found, 1 = key only, 2 = both key and value). */
988 extern int
ParseKeyValue(char ** ppch,char * apch[2])989 ParseKeyValue(char **ppch, char *apch[2])
990 {
991 
992     if (!ppch || !(apch[0] = NextToken(ppch)))
993         return 0;
994 
995     if (!(apch[1] = strchr(apch[0], '=')))
996         return 1;
997 
998     *apch[1] = 0;
999     apch[1]++;
1000     return 2;
1001 }
1002 
1003 /* Compare player names.  Performed case insensitively, and with all
1004  * whitespace characters and underscore considered identical. */
1005 extern int
CompareNames(char * sz0,char * sz1)1006 CompareNames(char *sz0, char *sz1)
1007 {
1008 
1009     static char ach[] = " \t\r\n\f\v_";
1010 
1011     for (; *sz0 || *sz1; sz0++, sz1++)
1012         if (toupper(*sz0) != toupper(*sz1) && (!strchr(ach, *sz0) || !strchr(ach, *sz1)))
1013             return toupper(*sz0) - toupper(*sz1);
1014 
1015     return 0;
1016 }
1017 
1018 extern void
UpdateSetting(void * p)1019 UpdateSetting(void *p)
1020 {
1021 #if defined(USE_GTK)
1022     if (fX)
1023         GTKSet(p);
1024 #else
1025     (void) p;                   /* suppress unused parameter compiler warning */
1026 #endif
1027 }
1028 
1029 extern void
UpdateSettings(void)1030 UpdateSettings(void)
1031 {
1032 
1033     UpdateSetting(&ms.nCube);
1034     UpdateSetting(&ms.fCubeOwner);
1035     UpdateSetting(&ms.fTurn);
1036     UpdateSetting(&ms.nMatchTo);
1037     UpdateSetting(&ms.fCrawford);
1038     UpdateSetting(&ms.fJacoby);
1039     UpdateSetting(&ms.gs);
1040 
1041     ShowBoard();
1042 
1043 #if defined(USE_BOARD3D)
1044     RestrictiveRedraw();
1045 #endif
1046 }
1047 
1048 /* handle turning a setting on / off
1049  * inputs: szName - the setting being adjusted
1050  * pf = pointer to current on/off state (will be updated)
1051  * sz = pointer to command line - a token will be extracted,
1052  * but furhter calls to NextToken will return only the on/off
1053  * value, so you can't have commands in the form
1054  * set something on <more tokens>
1055  * szOn - text to output when turning setting on
1056  * szOff - text to output when turning setting off
1057  * output: -1 on error
1058  * 0 setting is now off
1059  * 1 setting is now on
1060  * acceptable tokens are on/off yes/no true/false
1061  */
1062 extern int
SetToggle(const char * szName,int * pf,char * sz,const char * szOn,const char * szOff)1063 SetToggle(const char *szName, int *pf, char *sz, const char *szOn, const char *szOff)
1064 {
1065     char *pch = NextToken(&sz);
1066     size_t cch;
1067 
1068     if (!pch) {
1069         outputf(_("You must specify whether to set '%s' on or off.\n"), szName);
1070 
1071         return -1;
1072     }
1073 
1074     cch = strlen(pch);
1075 
1076     if (!StrCaseCmp("on", pch) || !StrNCaseCmp("yes", pch, cch) || !StrNCaseCmp("true", pch, cch)) {
1077         outputl(szOn);
1078 
1079         if (*pf != TRUE) {
1080             *pf = TRUE;
1081             UpdateSetting(pf);
1082         }
1083 
1084         return TRUE;
1085     }
1086 
1087     if (!StrCaseCmp("off", pch) || !StrNCaseCmp("no", pch, cch) || !StrNCaseCmp("false", pch, cch)) {
1088         outputl(szOff);
1089 
1090         if (*pf != FALSE) {
1091             *pf = FALSE;
1092             UpdateSetting(pf);
1093         }
1094 
1095         return FALSE;
1096     }
1097 
1098     outputf(_("Illegal keyword `%s'.\n"), pch);
1099 
1100     return -1;
1101 }
1102 
1103 /* FIXME? This is only used with fRestart == FALSE */
1104 extern void
PortableSignal(int nSignal,void (* p)(int),psighandler * pOld,int fRestart)1105 PortableSignal(int nSignal, void (*p) (int), psighandler * pOld, int fRestart)
1106 {
1107 #if defined(HAVE_SIGACTION)
1108     struct sigaction sa;
1109 
1110     sa.sa_handler = p;
1111     sigemptyset(&sa.sa_mask);
1112     sa.sa_flags =
1113 #if SA_RESTART
1114         (fRestart ? SA_RESTART : 0) |
1115 #endif
1116 #if SA_NOCLDSTOP
1117         SA_NOCLDSTOP |
1118 #endif
1119         0;
1120     sigaction(nSignal, p ? &sa : NULL, pOld);
1121 #elif defined(HAVE_SIGVEC)
1122     struct sigvec sv;
1123 
1124     sv.sv_handler = p;
1125     sigemptyset(&sv.sv_mask);
1126     sv.sv_flags = nSignal == SIGINT || nSignal == SIGIO ? SV_INTERRUPT : 0;
1127 
1128     sigvec(nSignal, p ? &sv : NULL, pOld);
1129 #else
1130     (void) fRestart;            /* silence compiler warning */
1131     if (pOld)
1132         *pOld = signal(nSignal, p);
1133     else if (p)
1134         signal(nSignal, p);
1135 #endif
1136 }
1137 
1138 extern void
PortableSignalRestore(int nSignal,psighandler * p)1139 PortableSignalRestore(int nSignal, psighandler * p)
1140 {
1141 #if defined(HAVE_SIGACTION)
1142     sigaction(nSignal, p, NULL);
1143 #elif defined(HAVE_SIGVEC)
1144     sigvec(nSignal, p, NULL);
1145 #else
1146     signal(nSignal, *p);
1147 #endif
1148 }
1149 
1150 /* Reset the SIGINT handler, on return to the main command loop.  Notify
1151  * the user if processing had been interrupted. */
1152 extern void
ResetInterrupt(void)1153 ResetInterrupt(void)
1154 {
1155     if (fInterrupt) {
1156         {
1157             outputf("(%s)", _("Interrupted"));
1158             outputx();
1159         }
1160 
1161         fInterrupt = FALSE;
1162 
1163         /* if  a batch was running signal a cancellation of current match */
1164         fMatchCancelled = TRUE;
1165 
1166 #if defined(USE_GTK)
1167         if (nNextTurn) {
1168             g_source_remove(nNextTurn);
1169             nNextTurn = 0;
1170         }
1171 #endif
1172     }
1173 }
1174 
1175 
1176 extern void
HandleCommand(char * sz,command * ac)1177 HandleCommand(char *sz, command * ac)
1178 {
1179 
1180     command *pc;
1181     char *pch;
1182     size_t cch;
1183 
1184     if (ac == acTop) {
1185         outputnew();
1186 
1187         if (*sz == '#')         /* Comment */
1188             return;
1189         else if (*sz == ':') {
1190             return;
1191         } else if (*sz == '>') {
1192 
1193             while (*sz == '>')
1194                 ++sz;
1195 
1196             /* leading white space confuses Python :-) */
1197 
1198             while (isspace(*sz))
1199                 ++sz;
1200 
1201 #if defined(USE_PYTHON)
1202             PythonRun(sz);
1203 #else
1204             outputl(_("This installation of GNU Backgammon was compiled without Python support."));
1205             outputx();
1206 #endif
1207             return;
1208         }
1209 
1210     }
1211 
1212     if (!(pch = NextToken(&sz))) {
1213         if (ac != acTop)
1214             outputl(_("Incomplete command."));
1215 
1216         outputx();
1217         return;
1218     }
1219 
1220     cch = strlen(pch);
1221 
1222     if (ac == acTop && (isdigit(*pch) || !StrNCaseCmp(pch, "bar/", cch > 4 ? 4 : cch))) {
1223         if (pch + cch < sz)
1224             pch[cch] = ' ';
1225 
1226         CommandMove(pch);
1227 
1228         outputx();
1229         return;
1230     }
1231 
1232     for (pc = ac; pc->sz; pc++)
1233         if (!StrNCaseCmp(pch, pc->sz, cch))
1234             break;
1235 
1236     if (!pc->sz) {
1237         if (!loading_rc)
1238             outputerrf(_("Unknown keyword `%s'.\n"), pch);
1239         return;
1240     }
1241 
1242     if (pc->pf) {
1243         pc->pf(sz);
1244 
1245         outputx();
1246     } else
1247         HandleCommand(sz, pc->pc);
1248 }
1249 
1250 extern void
InitBoard(TanBoard anBoard,const bgvariation bgv)1251 InitBoard(TanBoard anBoard, const bgvariation bgv)
1252 {
1253 
1254     switch (bgv) {
1255 
1256     case VARIATION_STANDARD:
1257         PositionFromID(anBoard, "4HPwATDgc/ABMA");
1258         break;
1259     case VARIATION_NACKGAMMON:
1260         PositionFromID(anBoard, "4Dl4ADbgOXgANg");
1261         break;
1262 
1263     case VARIATION_HYPERGAMMON_1:
1264         PositionFromID(anBoard, "AACAAAAAAgAAAA");
1265         break;
1266     case VARIATION_HYPERGAMMON_2:
1267         PositionFromID(anBoard, "AABAAQAACgAAAA");
1268         break;
1269     case VARIATION_HYPERGAMMON_3:
1270         PositionFromID(anBoard, "AACgAgAAKgAAAA");
1271         break;
1272 
1273     default:
1274         g_assert_not_reached();
1275 
1276     }
1277 
1278 }
1279 
1280 
1281 extern void
GetMatchStateCubeInfo(cubeinfo * pci,const matchstate * pms)1282 GetMatchStateCubeInfo(cubeinfo * pci, const matchstate * pms)
1283 {
1284 
1285     SetCubeInfo(pci, pms->nCube, pms->fCubeOwner, pms->fMove,
1286                 pms->nMatchTo, pms->anScore, pms->fCrawford, pms->fJacoby, nBeavers, pms->bgv);
1287 }
1288 
1289 static void
DisplayCubeAnalysis(float aarOutput[2][NUM_ROLLOUT_OUTPUTS],float aarStdDev[2][NUM_ROLLOUT_OUTPUTS],const evalsetup * pes)1290 DisplayCubeAnalysis(float aarOutput[2][NUM_ROLLOUT_OUTPUTS],
1291                     float aarStdDev[2][NUM_ROLLOUT_OUTPUTS], const evalsetup * pes)
1292 {
1293 
1294     cubeinfo ci;
1295 
1296     if (pes->et == EVAL_NONE)
1297         return;
1298 
1299     GetMatchStateCubeInfo(&ci, &ms);
1300 
1301     outputl(OutputCubeAnalysis(aarOutput, aarStdDev, pes, &ci));
1302 }
1303 
1304 extern char *
GetLuckAnalysis(const matchstate * pms,float rLuck)1305 GetLuckAnalysis(const matchstate * pms, float rLuck)
1306 {
1307 
1308     static char sz[16];
1309     cubeinfo ci;
1310 
1311     if (fOutputMWC && pms->nMatchTo) {
1312         GetMatchStateCubeInfo(&ci, pms);
1313 
1314         sprintf(sz, "%+0.3f%%", 100.0f * (eq2mwc(rLuck, &ci) - eq2mwc(0.0f, &ci)));
1315     } else
1316         sprintf(sz, "%+0.3f", rLuck);
1317 
1318     return sz;
1319 }
1320 
1321 static void
DisplayAnalysis(moverecord * pmr)1322 DisplayAnalysis(moverecord * pmr)
1323 {
1324 
1325     unsigned int i;
1326     char szBuf[1024];
1327 
1328     switch (pmr->mt) {
1329     case MOVE_NORMAL:
1330         DisplayCubeAnalysis(pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble);
1331 
1332         outputf("%s %u%u", _("Rolled"), pmr->anDice[0], pmr->anDice[1]);
1333 
1334         if (pmr->rLuck != ERR_VAL)
1335             outputf(" (%s):\n", GetLuckAnalysis(&ms, pmr->rLuck));
1336         else
1337             outputl(":");
1338 
1339         for (i = 0; i < pmr->ml.cMoves; i++) {
1340             if (i >= 10 /* FIXME allow user to choose limit */  &&
1341                 i != pmr->n.iMove)
1342                 continue;
1343             outputc(i == pmr->n.iMove ? '*' : ' ');
1344             output(FormatMoveHint(szBuf, &ms, &pmr->ml, i,
1345                                   i != pmr->n.iMove ||
1346                                   i != pmr->ml.cMoves - 1 || pmr->ml.cMoves == 1 || i < exsExport.nMoves, TRUE, TRUE));
1347 
1348         }
1349 
1350         break;
1351 
1352     case MOVE_DOUBLE:
1353         DisplayCubeAnalysis(pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble);
1354         break;
1355 
1356     case MOVE_TAKE:
1357     case MOVE_DROP:
1358         /* FIXME */
1359         break;
1360 
1361     case MOVE_SETDICE:
1362         if (pmr->rLuck != ERR_VAL)
1363             outputf("%s %u%u (%s):\n", _("Rolled"), pmr->anDice[0], pmr->anDice[1], GetLuckAnalysis(&ms, pmr->rLuck));
1364         break;
1365 
1366     default:
1367         break;
1368     }
1369 }
1370 
1371 extern void
ShowBoard(void)1372 ShowBoard(void)
1373 {
1374     char szBoard[2048];
1375     char sz[50], szCube[50], szPlayer0[MAX_NAME_LEN + 3], szPlayer1[MAX_NAME_LEN + 3],
1376         szScore0[50], szScore1[50], szMatch[50];
1377     char *apch[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
1378     moverecord *pmr;
1379     TanBoard an;
1380 
1381     if (cOutputDisabled || !foutput_on)
1382         return;
1383 
1384     if (ms.gs == GAME_NONE) {
1385 #if defined(USE_GTK)
1386         if (fX) {
1387             TanBoard anBoardTemp;
1388             InitBoard(anBoardTemp, ms.bgv);
1389             game_set(BOARD(pwBoard), anBoardTemp, 0, ap[1].szName,
1390                      ap[0].szName, ms.nMatchTo, ms.anScore[1], ms.anScore[0], 0, 0, FALSE, anChequers[ms.bgv]);
1391         } else
1392 #endif
1393 
1394             outputl(_("No game in progress."));
1395 
1396         return;
1397     }
1398 
1399     memcpy(an, msBoard(), sizeof(TanBoard));
1400     if (!ms.fMove)
1401         SwapSides(an);
1402 
1403 #if defined(USE_GTK)
1404     if (!fX) {
1405 #endif
1406         if (fOutputRawboard) {
1407             outputl(FIBSBoard(szBoard, an, ms.fMove, ap[1].szName,
1408                               ap[0].szName, ms.nMatchTo, ms.anScore[1],
1409                               ms.anScore[0], ms.anDice[0],
1410                               ms.anDice[1], ms.nCube,
1411                               ms.fCubeOwner, ms.fDoubled, ms.fTurn, ms.fCrawford, anChequers[ms.bgv],
1412                               ms.fPostCrawford));
1413 
1414             return;
1415         }
1416 
1417         apch[0] = szPlayer0;
1418         apch[6] = szPlayer1;
1419 
1420         sprintf(apch[1] = szScore0, ngettext("%d point", "%d points", ms.anScore[0]), ms.anScore[0]);
1421 
1422         sprintf(apch[5] = szScore1, ngettext("%d point", "%d points", ms.anScore[1]), ms.anScore[1]);
1423 
1424         if (ms.fDoubled) {
1425             apch[ms.fTurn ? 4 : 2] = szCube;
1426 
1427             sprintf(szPlayer0, "O: %s", ap[0].szName);
1428             sprintf(szPlayer1, "X: %s", ap[1].szName);
1429             sprintf(szCube, _("Cube offered at %d"), ms.nCube << 1);
1430         } else {
1431             sprintf(szPlayer0, "O: %s", ap[0].szName);
1432             sprintf(szPlayer1, "X: %s", ap[1].szName);
1433 
1434             apch[ms.fMove ? 4 : 2] = sz;
1435 
1436             if (ms.anDice[0])
1437                 sprintf(sz, _("%s %u%u"), _("Rolled"), ms.anDice[0], ms.anDice[1]);
1438             else if (!GameStatus(msBoard(), ms.bgv))
1439                 strcpy(sz, _("On roll"));
1440             else
1441                 sz[0] = 0;
1442 
1443             if (ms.fCubeOwner < 0) {
1444                 apch[3] = szCube;
1445 
1446                 if (ms.nMatchTo)
1447                     if (ms.nMatchTo == 1)
1448                         sprintf(szCube, _("1 point match"));
1449                     else if (ms.fCrawford)
1450                         sprintf(szCube, _("%d point match (Crawford game)"), ms.nMatchTo);
1451                     else
1452                         sprintf(szCube, _("%d point match (Cube: %d)"), ms.nMatchTo, ms.nCube);
1453                 else
1454                     sprintf(szCube, "(%s: %d)", _("Cube"), ms.nCube);
1455             } else {
1456                 size_t cch = strlen(ap[ms.fCubeOwner].szName);
1457 
1458                 if (cch > 20)
1459                     cch = 20;
1460 
1461                 sprintf(szCube, "%c: %*s (%s: %d)", ms.fCubeOwner ? 'X' : 'O',
1462                         (int) cch, ap[ms.fCubeOwner].szName, _("Cube"), ms.nCube);
1463 
1464                 apch[ms.fCubeOwner ? 6 : 0] = szCube;
1465 
1466                 if (ms.nMatchTo)
1467                     sprintf(apch[3] = szMatch, _("%d point match"), ms.nMatchTo);
1468             }
1469         }
1470         if (ms.fResigned > 0)
1471             /* FIXME it's not necessarily the player on roll who resigned */
1472             sprintf(strchr(sz, 0), ", %s %s", _("resigns"), gettext(aszGameResult[ms.fResigned - 1]));
1473 
1474         outputl(DrawBoard(szBoard, (ConstTanBoard) an, ms.fMove, apch, MatchIDFromMatchState(&ms), anChequers[ms.bgv]));
1475 
1476         if (
1477 #if defined(USE_GTK)
1478                PanelShowing(WINDOW_ANALYSIS) &&
1479 #endif
1480                plLastMove && (pmr = plLastMove->plNext->p)) {
1481             DisplayAnalysis(pmr);
1482             if (pmr->sz)
1483                 outputl(pmr->sz);       /* FIXME word wrap */
1484         }
1485 #if defined(USE_GTK)
1486     } else {
1487         game_set(BOARD(pwBoard), an, ms.fMove, ap[1].szName,
1488                  ap[0].szName, ms.nMatchTo, ms.anScore[1],
1489                  ms.anScore[0], ms.anDice[0], ms.anDice[1],
1490                  ap[ms.fTurn].pt != PLAYER_HUMAN && !fComputing && !nNextTurn, anChequers[ms.bgv]);
1491     }
1492 #endif
1493 
1494 #ifdef UNDEF
1495     {
1496         char *pc;
1497         printf("%s: %s\n", _("MatchID"), pc = MatchIDFromMatchState(&ms));
1498         MatchStateFromID(&ms, pc);
1499     }
1500 #endif
1501 }
1502 
1503 extern char *
FormatPrompt(void)1504 FormatPrompt(void)
1505 {
1506 
1507     static char sz[128];        /* FIXME check for overflow in rest of function */
1508     const char *pch = szPrompt;
1509     char *pchDest = sz;
1510     unsigned int anPips[2];
1511 
1512     while (*pch)
1513         if (*pch == '\\') {
1514             pch++;
1515             switch (*pch) {
1516             case 0:
1517                 goto done;
1518 
1519             case 'c':
1520             case 'C':
1521                 /* Pip count */
1522                 if (ms.gs == GAME_NONE)
1523                     strcpy(pchDest, _("No game"));
1524                 else {
1525                     PipCount(msBoard(), anPips);
1526                     sprintf(pchDest, "%u:%u", anPips[1], anPips[0]);
1527                 }
1528                 break;
1529 
1530             case 'p':
1531             case 'P':
1532                 /* Player on roll */
1533                 switch (ms.gs) {
1534                 case GAME_NONE:
1535                     strcpy(pchDest, _("No game"));
1536                     break;
1537 
1538                 case GAME_PLAYING:
1539                     strcpy(pchDest, ap[ms.fTurn].szName);
1540                     break;
1541 
1542                 case GAME_OVER:
1543                 case GAME_RESIGNED:
1544                 case GAME_DROP:
1545                     strcpy(pchDest, _("Game over"));
1546                     break;
1547                 }
1548                 break;
1549 
1550             case 's':
1551             case 'S':
1552                 /* Match score */
1553                 sprintf(pchDest, "%d:%d", ms.anScore[0], ms.anScore[1]);
1554                 break;
1555 
1556             case 'v':
1557             case 'V':
1558                 /* Version */
1559                 strcpy(pchDest, VERSION);
1560                 break;
1561 
1562             default:
1563                 *pchDest++ = *pch;
1564                 *pchDest = 0;
1565             }
1566 
1567             pchDest = strchr(pchDest, 0);
1568             pch++;
1569         } else
1570             *pchDest++ = *pch++;
1571 
1572   done:
1573     *pchDest = 0;
1574 
1575     return sz;
1576 }
1577 
1578 extern void
CommandEval(char * sz)1579 CommandEval(char *sz)
1580 {
1581 
1582     char szOutput[4096];
1583     int n;
1584     TanBoard an;
1585     cubeinfo ci;
1586     decisionData dd;
1587 
1588     if (!*sz && ms.gs == GAME_NONE) {
1589         outputl(_("No position specified and no game in progress."));
1590         return;
1591     }
1592 
1593     if ((n = ParsePosition(an, &sz, NULL)) < 0)
1594         return;
1595 
1596     if (n && ms.fMove)
1597         /* =n notation used; the opponent is on roll in the position given. */
1598         SwapSides(an);
1599 
1600     if (ms.gs == GAME_NONE)
1601         memcpy(&ci, &ciCubeless, sizeof(ci));
1602     else
1603         SetCubeInfo(&ci, ms.nCube, ms.fCubeOwner, n ? !ms.fMove : ms.fMove,
1604                     ms.nMatchTo, ms.anScore, ms.fCrawford, ms.fJacoby, nBeavers, ms.bgv);
1605 
1606     /* Consider cube action */
1607     dd.pboard = (ConstTanBoard) an;
1608     dd.pec = &GetEvalCube()->ec;
1609     dd.pci = &ci;
1610     dd.szOutput = szOutput;
1611     dd.n = n;
1612     if (RunAsyncProcess((AsyncFun) asyncDumpDecision, &dd, _("Evaluating position...")) == 0) {
1613 #if defined(USE_GTK)
1614         if (fX)
1615             GTKEval(szOutput);
1616         else
1617 #endif
1618             outputl(szOutput);
1619     }
1620 }
1621 
1622 extern command *
FindHelpCommand(command * pcBase,char * sz,char * pchCommand,char * pchUsage)1623 FindHelpCommand(command * pcBase, char *sz, char *pchCommand, char *pchUsage)
1624 {
1625 
1626     command *pc;
1627     const char *pch;
1628     size_t cch;
1629 
1630     if (!(pch = NextToken(&sz)))
1631         return pcBase;
1632 
1633     cch = strlen(pch);
1634 
1635     for (pc = pcBase->pc; pc && pc->sz; pc++)
1636         if (!StrNCaseCmp(pch, pc->sz, cch))
1637             break;
1638 
1639     if (!pc || !pc->sz)
1640         return NULL;
1641 
1642     pch = pc->sz;
1643     while (*pch)
1644         *pchCommand++ = *pchUsage++ = *pch++;
1645     *pchCommand++ = ' ';
1646     *pchCommand = 0;
1647     *pchUsage++ = ' ';
1648     *pchUsage = 0;
1649 
1650     if (pc->szUsage) {
1651         pch = gettext(pc->szUsage);
1652         while (*pch)
1653             *pchUsage++ = *pch++;
1654         *pchUsage++ = ' ';
1655         *pchUsage = 0;
1656     }
1657 
1658     if (pc->pc)
1659         /* subcommand */
1660         return FindHelpCommand(pc, sz, pchCommand, pchUsage);
1661     else
1662         /* terminal command */
1663         return pc;
1664 }
1665 
1666 extern char *
CheckCommand(char * sz,command * ac)1667 CheckCommand(char *sz, command * ac)
1668 {
1669     command *pc;
1670     size_t cch;
1671     char *pch = NextToken(&sz);
1672     if (!pch)
1673         return 0;
1674 
1675     cch = strlen(pch);
1676     for (pc = ac; pc->sz; pc++)
1677         if (!StrNCaseCmp(pch, pc->sz, cch))
1678             break;
1679     if (!pc->sz)
1680         return pch;
1681 
1682     if (pc->pf) {
1683         return 0;
1684     } else {
1685         return CheckCommand(sz, pc->pc);
1686     }
1687 }
1688 
1689 extern void
CommandHelp(char * sz)1690 CommandHelp(char *sz)
1691 {
1692 
1693     command *pc, *pcFull;
1694     char szCommand[128], szUsage[128], *szHelp;
1695 
1696 #if defined(USE_GTK)
1697     if (fX) {
1698         GTKHelp(sz);
1699         return;
1700     }
1701 #endif
1702 
1703     if (!(pc = FindHelpCommand(&cTop, sz, szCommand, szUsage))) {
1704         outputf(_("No help available for topic `%s'"), sz);
1705         output("\n");
1706 
1707         return;
1708     }
1709 
1710     if (pc->szHelp)
1711         /* the command has its own help text */
1712         szHelp = gettext(pc->szHelp);
1713     else if (pc == &cTop)
1714         /* top-level help isn't for any command */
1715         szHelp = NULL;
1716     else {
1717         /* perhaps the command is an abbreviation; search for the full
1718          * version */
1719         szHelp = NULL;
1720 
1721         for (pcFull = acTop; pcFull->sz; pcFull++)
1722             if (pcFull->pf == pc->pf && pcFull->szHelp) {
1723                 szHelp = gettext(pcFull->szHelp);
1724                 break;
1725             }
1726     }
1727 
1728     if (szHelp) {
1729         outputf("%s- %s\n\n%s: %s", szCommand, szHelp, _("Usage"), szUsage);
1730 
1731         if (pc->pc && pc->pc->sz)
1732             outputf("<%s>\n", _("subcommand"));
1733         else
1734             outputc('\n');
1735     }
1736 
1737     if (pc->pc && pc->pc->sz) {
1738         outputl(pc == &cTop ? _("Available commands:") : _("Available subcommands:"));
1739 
1740         pc = pc->pc;
1741 
1742         for (; pc->sz; pc++)
1743             if (pc->szHelp)
1744                 outputf("%-15s\t%s\n", pc->sz, gettext(pc->szHelp));
1745     }
1746 }
1747 
1748 
1749 extern char *
FormatMoveHint(char * sz,const matchstate * pms,movelist * pml,int i,int fRankKnown,int fDetailProb,int fShowParameters)1750 FormatMoveHint(char *sz, const matchstate * pms, movelist * pml,
1751                int i, int fRankKnown, int fDetailProb, int fShowParameters)
1752 {
1753 
1754     cubeinfo ci;
1755     char szTemp[2048], szMove[32];
1756     float *ar, *arStdDev;
1757     float rEq, rEqTop;
1758 
1759     GetMatchStateCubeInfo(&ci, pms);
1760 
1761     strcpy(sz, "");
1762 
1763     /* number */
1764 
1765     if (i && !fRankKnown)
1766         strcat(sz, "   ??  ");
1767     else
1768         sprintf(strchr(sz, 0), " %4i. ", i + 1);
1769 
1770     /* eval */
1771 
1772     sprintf(strchr(sz, 0),
1773             "%-14s   %-28s %s: ",
1774             FormatEval(szTemp, &pml->amMoves[i].esMove),
1775             FormatMove(szMove, pms->anBoard,
1776                        pml->amMoves[i].anMove), (!pms->nMatchTo || !fOutputMWC) ? _("Eq.") : _("MWC"));
1777 
1778     /* equity or mwc for move */
1779 
1780     ar = pml->amMoves[i].arEvalMove;
1781     arStdDev = pml->amMoves[i].arEvalStdDev;
1782     rEq = pml->amMoves[i].rScore;
1783     rEqTop = pml->amMoves[0].rScore;
1784 
1785     strcat(sz, OutputEquity(rEq, &ci, TRUE));
1786 
1787     /* difference */
1788 
1789     if (i)
1790         sprintf(strchr(sz, 0), " (%s)\n", OutputEquityDiff(rEq, rEqTop, &ci));
1791     else
1792         strcat(sz, "\n");
1793 
1794     /* percentages */
1795 
1796     if (fDetailProb) {
1797 
1798         switch (pml->amMoves[i].esMove.et) {
1799         case EVAL_EVAL:
1800             /* FIXME: add cubeless and cubeful equities */
1801             strcat(sz, "       ");
1802             strcat(sz, OutputPercents(ar, TRUE));
1803             strcat(sz, "\n");
1804             break;
1805         case EVAL_ROLLOUT:
1806             strcat(sz, OutputRolloutResult("     ", NULL, (float (*)[NUM_ROLLOUT_OUTPUTS])
1807                                            ar, (float (*)[NUM_ROLLOUT_OUTPUTS])
1808                                            arStdDev, &ci, 0, 1, pml->amMoves[i].esMove.rc.fCubeful));
1809             break;
1810         default:
1811             break;
1812 
1813         }
1814     }
1815 
1816     /* eval parameters */
1817 
1818     if (fShowParameters) {
1819 
1820         switch (pml->amMoves[i].esMove.et) {
1821         case EVAL_EVAL:
1822             strcat(sz, "        ");
1823             strcat(sz, OutputEvalContext(&pml->amMoves[i].esMove.ec, TRUE));
1824             strcat(sz, "\n");
1825             break;
1826         case EVAL_ROLLOUT:
1827             strcat(sz, OutputRolloutContext("        ", &pml->amMoves[i].esMove.rc));
1828             break;
1829 
1830         default:
1831             break;
1832 
1833         }
1834 
1835     }
1836 
1837     return sz;
1838 
1839 #if 0
1840 
1841 
1842     if (!pms->nMatchTo || (pms->nMatchTo && !fOutputMWC)) {
1843         /* output in equity */
1844         float *ar, rEq, rEqTop;
1845 
1846         ar = pml->amMoves[0].arEvalMove;
1847         rEqTop = pml->amMoves[0].rScore;
1848 
1849         if (!i) {
1850             if (fOutputWinPC)
1851                 sprintf(sz, " %4i. %-14s   %-28s Eq.: %+6.3f\n"
1852                         "       %5.1f%% %5.1f%% %5.1f%%  -"
1853                         " %5.1f%% %5.1f%% %5.1f%%\n",
1854                         1, FormatEval(szTemp, &pml->amMoves[0].esMove),
1855                         FormatMove(szMove, pms->anBoard,
1856                                    pml->amMoves[0].anMove),
1857                         rEqTop,
1858                         100.0 * ar[0], 100.0 * ar[1], 100.0 * ar[2],
1859                         100.0 * (1.0 - ar[0]), 100.0 * ar[3], 100.0 * ar[4]);
1860             else
1861                 sprintf(sz, " %4i. %-14s   %-28s Eq.: %+6.3f\n"
1862                         "       %5.3f %5.3f %5.3f  -"
1863                         " %5.3f %5.3f %5.3f\n",
1864                         1, FormatEval(szTemp, &pml->amMoves[0].esMove),
1865                         FormatMove(szMove, pms->anBoard,
1866                                    pml->amMoves[0].anMove), rEqTop, ar[0], ar[1], ar[2], (1.0 - ar[0]), ar[3], ar[4]);
1867         } else {
1868             ar = pml->amMoves[i].arEvalMove;
1869             rEq = pml->amMoves[i].rScore;
1870 
1871             if (fRankKnown)
1872                 sprintf(sz, " %4i.", i + 1);
1873             else
1874                 strcpy(sz, "   ?? ");
1875 
1876             if (fOutputWinPC)
1877                 sprintf(sz + 6, " %-14s   %-28s Eq.: %+6.3f (%+6.3f)\n"
1878                         "       %5.1f%% %5.1f%% %5.1f%%  -"
1879                         " %5.1f%% %5.1f%% %5.1f%%\n",
1880                         FormatEval(szTemp, &pml->amMoves[i].esMove),
1881                         FormatMove(szMove, pms->anBoard,
1882                                    pml->amMoves[i].anMove),
1883                         rEq, rEq - rEqTop,
1884                         100.0 * ar[0], 100.0 * ar[1], 100.0 * ar[2],
1885                         100.0 * (1.0 - ar[0]), 100.0 * ar[3], 100.0 * ar[4]);
1886             else
1887                 sprintf(sz + 6, " %-14s   %-28s Eq.: %+6.3f (%+6.3f)\n"
1888                         "       %5.3f %5.3f %5.3f  -"
1889                         " %5.3f %5.3f %5.3f\n",
1890                         FormatEval(szTemp, &pml->amMoves[i].esMove),
1891                         FormatMove(szMove, pms->anBoard,
1892                                    pml->amMoves[i].anMove),
1893                         rEq, rEq - rEqTop, ar[0], ar[1], ar[2], (1.0 - ar[0]), ar[3], ar[4]);
1894         }
1895     } else {
1896         /* output in mwc */
1897 
1898         float *ar, rMWC, rMWCTop;
1899 
1900         ar = pml->amMoves[0].arEvalMove;
1901         rMWCTop = 100.0 * eq2mwc(pml->amMoves[0].rScore, &ci);
1902 
1903         if (!i) {
1904             if (fOutputWinPC)
1905                 sprintf(sz, " %4i. %-14s   %-28s MWC: %7.3f%%\n"
1906                         "       %5.1f%% %5.1f%% %5.1f%%  -"
1907                         " %5.1f%% %5.1f%% %5.1f%%\n",
1908                         1, FormatEval(szTemp, &pml->amMoves[0].esMove),
1909                         FormatMove(szMove, pms->anBoard,
1910                                    pml->amMoves[0].anMove),
1911                         rMWCTop,
1912                         100.0 * ar[0], 100.0 * ar[1], 100.0 * ar[2],
1913                         100.0 * (1.0 - ar[0]), 100.0 * ar[3], 100.0 * ar[4]);
1914             else
1915                 sprintf(sz, " %4i. %-14s   %-28s MWC: %7.3f%%\n"
1916                         "       %5.3f %5.3f %5.3f  -"
1917                         " %5.3f %5.3f %5.3f\n",
1918                         1, FormatEval(szTemp, &pml->amMoves[0].esMove),
1919                         FormatMove(szMove, pms->anBoard,
1920                                    pml->amMoves[0].anMove), rMWCTop, ar[0], ar[1], ar[2], (1.0 - ar[0]), ar[3], ar[4]);
1921         } else {
1922             ar = pml->amMoves[i].arEvalMove;
1923             rMWC = 100.0 * eq2mwc(pml->amMoves[i].rScore, &ci);
1924 
1925             if (fRankKnown)
1926                 sprintf(sz, " %4i.", i + 1);
1927             else
1928                 strcpy(sz, "   ?? ");
1929 
1930             if (fOutputWinPC)
1931                 sprintf(sz + 6, " %-14s   %-28s MWC: %7.3f%% (%+7.3f%%)\n"
1932                         "       %5.1f%% %5.1f%% %5.1f%%  -"
1933                         " %5.1f%% %5.1f%% %5.1f%%\n",
1934                         FormatEval(szTemp, &pml->amMoves[i].esMove),
1935                         FormatMove(szMove, pms->anBoard,
1936                                    pml->amMoves[i].anMove),
1937                         rMWC, rMWC - rMWCTop,
1938                         100.0 * ar[0], 100.0 * ar[1], 100.0 * ar[2],
1939                         100.0 * (1.0 - ar[0]), 100.0 * ar[3], 100.0 * ar[4]);
1940             else
1941                 sprintf(sz + 6, " %-14s   %-28s MWC: %7.3f%% (%+7.3f%%)\n"
1942                         "       %5.3f %5.3f %5.3f  -"
1943                         " %5.3f %5.3f %5.3f\n",
1944                         FormatEval(szTemp, &pml->amMoves[i].esMove),
1945                         FormatMove(szMove, pms->anBoard,
1946                                    pml->amMoves[i].anMove),
1947                         rMWC, rMWC - rMWCTop, ar[0], ar[1], ar[2], (1.0 - ar[0]), ar[3], ar[4]);
1948         }
1949     }
1950 
1951     return sz;
1952 #endif
1953 }
1954 
1955 
1956 static void
HintResigned(void)1957 HintResigned(void)
1958 {
1959     float rEqBefore, rEqAfter;
1960     static cubeinfo ci;
1961     static decisionData dd;
1962 
1963     GetMatchStateCubeInfo(&ci, &ms);
1964 
1965 #if defined(USE_BOARD3D)
1966     if (fX) {                   /* Stop waving flag, otherwise hangs */
1967         BoardData *bd = BOARD(pwBoard)->board_data;
1968         StopIdle3d(bd, bd->bd3d);
1969     }
1970 #endif
1971 
1972     /* evaluate current position */
1973 
1974     dd.pboard = msBoard();
1975     dd.pci = &ci;
1976     dd.pec = &GetEvalCube()->ec;
1977     if (RunAsyncProcess((AsyncFun) asyncMoveDecisionE, &dd, _("Considering resignation...")) != 0)
1978         return;
1979 
1980     getResignEquities(dd.aarOutput[0], &ci, ms.fResigned, &rEqBefore, &rEqAfter);
1981 
1982 #if defined(USE_GTK)
1983     if (fX) {
1984 
1985         GTKResignHint(dd.aarOutput[0], rEqBefore, rEqAfter, &ci, ms.nMatchTo && fOutputMWC);
1986 
1987         return;
1988 
1989     }
1990 #endif
1991 
1992     if (!ms.nMatchTo || !fOutputMWC) {
1993 
1994         outputf("%s : %+6.3f\n", _("Equity before resignation"), -rEqBefore);
1995         outputf("%s : %+6.3f (%+6.3f)\n\n", _("Equity after resignation"), -rEqAfter, rEqBefore - rEqAfter);
1996         outputf("%s : %s\n\n", _("Correct resign decision"), (rEqBefore - rEqAfter >= 0) ? _("Accept") : _("Reject"));
1997 
1998     } else {
1999 
2000         rEqBefore = eq2mwc(-rEqBefore, &ci);
2001         rEqAfter = eq2mwc(-rEqAfter, &ci);
2002 
2003         outputf("%s : %6.2f%%\n", _("Equity before resignation"), rEqBefore * 100.0f);
2004         outputf("%s : %6.2f%% (%6.2f%%)\n\n", _("Equity after resignation"),
2005                 rEqAfter * 100.0f, 100.0f * (rEqAfter - rEqBefore));
2006         outputf("%s : %s\n\n", _("Correct resign decision"), (rEqAfter - rEqBefore >= 0) ? _("Accept") : _("Reject"));
2007     }
2008 }
2009 
2010 static int
hint_cube(moverecord * pmr,cubeinfo * pci)2011 hint_cube(moverecord * pmr, cubeinfo * pci)
2012 {
2013     static decisionData dd;
2014     if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE) {
2015         /* no analysis performed yet */
2016         dd.pboard = msBoard();
2017         dd.pci = pci;
2018         dd.pes = GetEvalCube();
2019         if (RunAsyncProcess((AsyncFun) asyncCubeDecision, &dd, _("Considering cube action...")) != 0)
2020             return -1;
2021 
2022         pmr_cubedata_set(pmr, dd.pes, dd.aarOutput, dd.aarStdDev);
2023     }
2024     return 0;
2025 }
2026 
2027 static skilltype
no_double_skill(moverecord * pmr,cubeinfo * pci)2028 no_double_skill(moverecord * pmr, cubeinfo * pci)
2029 {
2030     float arDouble[4];
2031     float eq = 0.0f;
2032     cubedecision cd;
2033     if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
2034         return SKILL_NONE;
2035     cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
2036     switch (cd) {
2037     case DOUBLE_TAKE:
2038     case DOUBLE_BEAVER:
2039     case REDOUBLE_TAKE:
2040         eq = arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_TAKE];
2041         break;
2042     case DOUBLE_PASS:
2043     case REDOUBLE_PASS:
2044         eq = arDouble[OUTPUT_NODOUBLE] - arDouble[OUTPUT_DROP];
2045         break;
2046     default:
2047         break;
2048     }
2049 
2050     return Skill(eq);
2051 }
2052 
2053 static skilltype
double_skill(moverecord * pmr,cubeinfo * pci)2054 double_skill(moverecord * pmr, cubeinfo * pci)
2055 {
2056     float arDouble[4];
2057     float eq = 0.0f;
2058     cubedecision cd;
2059     if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
2060         return SKILL_NONE;
2061     cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
2062 
2063     switch (cd) {
2064     case NODOUBLE_TAKE:
2065     case NODOUBLE_BEAVER:
2066     case NO_REDOUBLE_TAKE:
2067     case NO_REDOUBLE_BEAVER:
2068     case TOOGOOD_TAKE:
2069     case TOOGOODRE_TAKE:
2070         eq = arDouble[OUTPUT_TAKE] - arDouble[OUTPUT_NODOUBLE];
2071         break;
2072 
2073     case TOOGOOD_PASS:
2074     case TOOGOODRE_PASS:
2075         eq = arDouble[OUTPUT_DROP] - arDouble[OUTPUT_NODOUBLE];
2076         break;
2077     default:
2078         break;
2079     }
2080     return Skill(eq);
2081 }
2082 
2083 static skilltype
drop_skill(moverecord * pmr,cubeinfo * pci)2084 drop_skill(moverecord * pmr, cubeinfo * pci)
2085 {
2086     float arDouble[4];
2087     float eq = 0.0f;
2088     cubedecision cd;
2089     if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
2090         return SKILL_NONE;
2091     cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
2092     switch (cd) {
2093     case DOUBLE_TAKE:
2094     case DOUBLE_BEAVER:
2095     case REDOUBLE_TAKE:
2096     case NODOUBLE_TAKE:
2097     case NODOUBLE_BEAVER:
2098     case NO_REDOUBLE_TAKE:
2099     case NO_REDOUBLE_BEAVER:
2100     case TOOGOOD_TAKE:
2101     case TOOGOODRE_TAKE:
2102     case OPTIONAL_DOUBLE_BEAVER:
2103     case OPTIONAL_DOUBLE_TAKE:
2104     case OPTIONAL_REDOUBLE_TAKE:
2105         /* equity is for doubling player, invert for response */
2106         eq = arDouble[OUTPUT_TAKE] - arDouble[OUTPUT_DROP];
2107         break;
2108     default:
2109         break;
2110     }
2111     return Skill(eq);
2112 }
2113 
2114 static skilltype
take_skill(moverecord * pmr,cubeinfo * pci)2115 take_skill(moverecord * pmr, cubeinfo * pci)
2116 {
2117     float arDouble[4];
2118     float eq = 0.0f;
2119     cubedecision cd;
2120     if (pmr->CubeDecPtr->esDouble.et == EVAL_NONE)
2121         return SKILL_NONE;
2122     cd = FindCubeDecision(arDouble, pmr->CubeDecPtr->aarOutput, pci);
2123     switch (cd) {
2124     case DOUBLE_PASS:
2125     case REDOUBLE_PASS:
2126     case TOOGOOD_PASS:
2127     case TOOGOODRE_PASS:
2128     case OPTIONAL_DOUBLE_PASS:
2129     case OPTIONAL_REDOUBLE_PASS:
2130         /* equity is for doubling player, invert for response */
2131         eq = arDouble[OUTPUT_DROP] - arDouble[OUTPUT_TAKE];
2132         break;
2133     default:
2134         break;
2135     }
2136     return Skill(eq);
2137 }
2138 
2139 static skilltype
move_skill(moverecord * pmr)2140 move_skill(moverecord * pmr)
2141 {
2142     move *move_i;
2143     move *move_0;
2144     if (pmr->n.iMove >= pmr->ml.cMoves || !pmr->ml.amMoves)
2145         return SKILL_NONE;
2146     move_i = &pmr->ml.amMoves[pmr->n.iMove];
2147     move_0 = &pmr->ml.amMoves[0];
2148     if (move_i->esMove.et == EVAL_NONE || move_0->esMove.et == EVAL_NONE)
2149         return SKILL_NONE;
2150     else
2151         return Skill(move_i->rScore - move_0->rScore);
2152 }
2153 
2154 extern void
find_skills(moverecord * pmr,const matchstate * pms,int did_double,int did_take)2155 find_skills(moverecord * pmr, const matchstate * pms, int did_double, int did_take)
2156 {
2157     cubeinfo ci;
2158     doubletype dt = DoubleType(pms->fDoubled, pms->fMove, pms->fTurn);
2159     taketype tt = (taketype) dt;
2160     GetMatchStateCubeInfo(&ci, pms);
2161 
2162     if (pmr->mt != MOVE_NORMAL && pmr->mt != MOVE_DOUBLE && pmr->mt != MOVE_TAKE && pmr->mt != MOVE_DROP) {
2163         pmr->n.stMove = SKILL_NONE;
2164         pmr->stCube = SKILL_NONE;
2165         return;
2166     }
2167     if (pmr->mt == MOVE_DOUBLE && dt != DT_NORMAL) {
2168         pmr->stCube = SKILL_NONE;
2169         return;
2170     }
2171     if (pmr->mt == MOVE_TAKE && tt > TT_NORMAL) {
2172         pmr->stCube = SKILL_NONE;
2173         return;
2174     }
2175 
2176     if (did_double == FALSE)
2177         pmr->stCube = no_double_skill(pmr, &ci);
2178     else if (did_double == TRUE)
2179         pmr->stCube = double_skill(pmr, &ci);
2180     else if (did_take == FALSE)
2181         pmr->stCube = drop_skill(pmr, &ci);
2182     else if (did_take == TRUE)
2183         pmr->stCube = take_skill(pmr, &ci);
2184 
2185     if (pmr->mt == MOVE_NORMAL && pmr->ml.cMoves > 0 && pmr->n.iMove < pmr->ml.cMoves)
2186         pmr->n.stMove = move_skill(pmr);
2187 
2188 }
2189 
2190 extern void
hint_double(int show,int did_double)2191 hint_double(int show, int did_double)
2192 {
2193     static cubeinfo ci;
2194     moverecord *pmr;
2195     int hist;
2196     doubletype dt = DoubleType(ms.fDoubled, ms.fMove, ms.fTurn);
2197     if (dt != DT_NORMAL) {
2198         if (show)
2199             outputerrf(_("This decision is part of beaver/raccoon sequence and cannot be hinted"));
2200         return;
2201     }
2202 
2203     GetMatchStateCubeInfo(&ci, &ms);
2204 
2205     if (!GetDPEq(NULL, NULL, &ci)) {
2206         outputerrf(_("You cannot double."));
2207         return;
2208     }
2209 
2210     pmr = get_current_moverecord(&hist);
2211 
2212     if (!pmr)
2213         return;
2214 
2215     if (hint_cube(pmr, &ci) < 0)
2216         return;
2217 
2218     if (hist && did_double == -1)
2219         did_double = (pmr->mt == MOVE_DOUBLE) ? TRUE : FALSE;
2220 
2221     find_skills(pmr, &ms, did_double, -1);
2222 
2223 #if defined(USE_GTK)
2224     if (fX) {
2225         if (hist && show)
2226             ChangeGame(NULL);
2227         if (show)
2228             GTKCubeHint(pmr, &ms, did_double, -1, hist);
2229         return;
2230     }
2231 #endif
2232     outputl(OutputCubeAnalysis
2233             (pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble, &ci));
2234 }
2235 
2236 extern void
hint_take(int show,int did_take)2237 hint_take(int show, int did_take)
2238 {
2239     static cubeinfo ci;
2240     moverecord *pmr;
2241     int hist;
2242     taketype tt = (taketype) DoubleType(ms.fDoubled, ms.fMove, ms.fTurn);
2243     if (tt > TT_NORMAL) {
2244         if (show)
2245             outputerrf(_("This decision is part of beaver/raccoon sequence and cannot be hinted"));
2246         return;
2247     }
2248 
2249     GetMatchStateCubeInfo(&ci, &ms);
2250     pmr = get_current_moverecord(&hist);
2251     if (!pmr)
2252         return;
2253     if (hint_cube(pmr, &ci) < 0)
2254         return;
2255 
2256     if (hist && did_take == -1)
2257         did_take = (pmr->mt == MOVE_TAKE) ? TRUE : FALSE;
2258 
2259     find_skills(pmr, &ms, -1, did_take);
2260 
2261 #if defined(USE_GTK)
2262     if (fX) {
2263         if (hist && show)
2264             ChangeGame(NULL);
2265         if (show)
2266             GTKCubeHint(pmr, &ms, -1, did_take, hist);
2267         return;
2268     }
2269 #endif
2270 
2271     outputl(OutputCubeAnalysis
2272             (pmr->CubeDecPtr->aarOutput, pmr->CubeDecPtr->aarStdDev, &pmr->CubeDecPtr->esDouble, &ci));
2273 }
2274 
2275 extern void
hint_move(char * sz,gboolean show,procrecorddata * procdatarec)2276 hint_move(char *sz, gboolean show, procrecorddata * procdatarec)
2277 {
2278     unsigned int i;
2279     char szBuf[1024];
2280     int parse_n = ParseNumber(&sz);
2281     unsigned int n = (parse_n <= 0) ? 10 : parse_n;
2282     moverecord *pmr;
2283     cubeinfo ci;
2284     int hist;
2285     movelist ml;
2286     findData fd;
2287     int fSaveShowProg = fShowProgress;
2288 
2289     if (!ms.anDice[0])
2290         return;
2291 
2292     GetMatchStateCubeInfo(&ci, &ms);
2293 
2294     pmr = get_current_moverecord(&hist);
2295     if (!pmr)
2296         return;
2297 
2298     if (pmr->esChequer.et == EVAL_NONE) {
2299         fd.pml = &ml;
2300         fd.pboard = msBoard();
2301         fd.keyMove = NULL;
2302         fd.rThr = arSkillLevel[SKILL_DOUBTFUL];
2303         fd.pci = &ci;
2304         fd.pec = &GetEvalChequer()->ec;
2305         fd.aamf = *GetEvalMoveFilter();
2306         if (procdatarec) {
2307             show = FALSE;
2308             fShowProgress = (procdatarec->avInputData[PROCREC_HINT_ARGIN_SHOWPROGRESS] != NULL);
2309         }
2310         if ((RunAsyncProcess((AsyncFun) asyncFindMove, &fd, _("Considering move...")) != 0) || fInterrupt) {
2311             fShowProgress = fSaveShowProg;
2312             return;
2313         }
2314         fShowProgress = fSaveShowProg;
2315 
2316         pmr_movelist_set(pmr, GetEvalChequer(), &ml);
2317     }
2318 #if defined(USE_GTK)
2319     if (!hist && fX)
2320         GTKGetMove(pmr->n.anMove);
2321 #endif
2322     if (pmr->n.anMove[0] == -1) {
2323         pmr->n.iMove = UINT_MAX;
2324         pmr->n.stMove = SKILL_NONE;
2325     } else {
2326         pmr->n.iMove = locateMove(msBoard(), pmr->n.anMove, &pmr->ml);
2327         /* Tutor mode may have called asyncFindMove() above before
2328          * n.iMove was known. Do it again, ensuring that the actual
2329          * move is evaluated at the best ply. */
2330         fd.pml = &ml;
2331         fd.pboard = msBoard();
2332         fd.keyMove = &(pmr->ml.amMoves[pmr->n.iMove].key);
2333         fd.rThr = arSkillLevel[SKILL_DOUBTFUL];
2334         fd.pci = &ci;
2335         fd.pec = &GetEvalChequer()->ec;
2336         fd.aamf = *GetEvalMoveFilter();
2337         asyncFindMove(&fd);
2338         pmr_movelist_set(pmr, GetEvalChequer(), &ml);
2339         find_skills(pmr, &ms, FALSE, -1);
2340     }
2341 #if defined(USE_GTK)
2342     if (!procdatarec && fX) {
2343         if (hist && show)
2344             ChangeGame(NULL);
2345         if (show)
2346             GTKHint(pmr, hist);
2347         return;
2348     } else
2349 #endif
2350     if (!procdatarec && !show)
2351         return;
2352 
2353     if (!procdatarec && show && !pmr->ml.cMoves) {
2354         outputl(_("There are no legal moves."));
2355         return;
2356     }
2357 
2358     if (procdatarec) {
2359         procdatarec->avOutputData[PROCREC_HINT_ARGOUT_MATCHSTATE] = (void *) &ms;
2360         procdatarec->avOutputData[PROCREC_HINT_ARGOUT_CUBEINFO] = (void *) &ci;
2361         procdatarec->avOutputData[PROCREC_HINT_ARGOUT_MOVELIST] = (void *) &pmr->ml;
2362         procdatarec->avOutputData[PROCREC_HINT_ARGOUT_MOVERECORD] = (void *) &pmr;
2363     }
2364 
2365     n = MIN(pmr->ml.cMoves, n);
2366     for (i = 0; i < n; i++) {
2367         if (show)
2368             output(FormatMoveHint(szBuf, &ms, &pmr->ml, i, TRUE, TRUE, TRUE));
2369         else if (procdatarec && procdatarec->pfProcessRecord) {
2370             procdatarec->avOutputData[PROCREC_HINT_ARGOUT_INDEX] = (void *) (ptrdiff_t) i;
2371             if (!procdatarec->pfProcessRecord(procdatarec))
2372                 break;
2373         }
2374     }
2375 }
2376 
2377 extern void
CommandHint(char * sz)2378 CommandHint(char *sz)
2379 {
2380     listOLD *pl;
2381     moverecord *pmr;
2382 
2383     if (ms.gs != GAME_PLAYING) {
2384         outputl(_("You must set up a board first."));
2385 
2386         return;
2387     }
2388 
2389     /* The code further below handles only the case of asking a hint
2390      * on a resignation by gnubg while playing against it. When
2391      * clicking on a "resign" entry in the moves panel while reviewing
2392      * a game, the match state ms is not up to date and we have to
2393      * look at the moves list */
2394 
2395     pl = plLastMove->plNext;
2396     if ((pmr = pl->p) && pmr->mt == MOVE_RESIGN) {
2397         HintResigned();
2398         return;
2399     }
2400 
2401     /* hint on cube decision */
2402 
2403     if (!ms.anDice[0] && !ms.fDoubled && !ms.fResigned) {
2404         hint_double(TRUE, -1);
2405         return;
2406     }
2407 
2408     /* Give hint on resignation */
2409 
2410     if (ms.fResigned) {
2411         HintResigned();
2412         return;
2413     }
2414 
2415     /* Give hint on take decision */
2416 
2417     if (ms.fDoubled) {
2418         hint_take(TRUE, -1);
2419         return;
2420     }
2421 
2422     /* Give hint on chequer play decision */
2423 
2424     if (ms.anDice[0]) {
2425         hint_move(sz, TRUE, NULL);
2426         return;
2427     }
2428 
2429 }
2430 
2431 static void
Shutdown(void)2432 Shutdown(void)
2433 {
2434 
2435     RenderFinalise();
2436 
2437     free_rngctx(rngctxCurrent);
2438     free_rngctx(rngctxRollout);
2439 
2440     FreeMatch();
2441     ClearMatch();
2442 
2443 #if defined(USE_GTK)
2444     MoveListDestroy();
2445 #endif
2446 
2447     MT_Close();
2448 
2449     EvalShutdown();
2450 
2451 #if defined(USE_PYTHON)
2452     PythonShutdown();
2453 #endif
2454 
2455 #if defined(LIBCURL_PROTOCOL_HTTPS)
2456     /* Cleanup curl */
2457     curl_global_cleanup();
2458 #endif
2459 
2460 #if defined(HAVE_SOCKETS)
2461 #ifdef WIN32
2462     WSACleanup();
2463 #endif
2464 #endif
2465 
2466     SoundWait();
2467 }
2468 
2469 /* Called on various exit commands -- e.g. EOF on stdin, "quit" command,
2470  * etc.  If stdin is not a TTY, this should always exit immediately (to
2471  * avoid enless loops on EOF).  If stdin is a TTY, and fConfirmNew is set,
2472  * and a game is in progress, then we ask the user if they're sure. */
2473 extern void
PromptForExit(void)2474 PromptForExit(void)
2475 {
2476 
2477     static int fExiting = FALSE;
2478 #if defined(USE_GTK)
2479     BoardData *bd = NULL;
2480 
2481     if (fX) {
2482         bd = BOARD(pwBoard)->board_data;
2483         g_assert(bd);
2484     }
2485 #endif
2486     if (fExiting)
2487         return;
2488 
2489     fExiting = TRUE;
2490 
2491     if (fInteractive) {
2492         fInterrupt = FALSE;
2493         if (!get_input_discard()) {
2494             fInterrupt = FALSE;
2495             fExiting = FALSE;
2496             return;
2497         }
2498     }
2499 #if defined(USE_BOARD3D)
2500     if (fX && bd && (display_is_3d(bd->rd))) {  /* Stop any 3d animations */
2501         StopIdle3d(bd, bd->bd3d);
2502     }
2503 #endif
2504 #if defined(HAVE_SOCKETS)
2505     /* Close any open connections */
2506     if (ap[0].pt == PLAYER_EXTERNAL)
2507         closesocket(ap[0].h);
2508     if (ap[1].pt == PLAYER_EXTERNAL)
2509         closesocket(ap[1].h);
2510 #endif
2511 
2512     playSound(SOUND_EXIT);
2513 
2514 #if defined(USE_BOARD3D)
2515     if (fX && bd && display_is_3d(bd->rd) && bd->rd->closeBoardOnExit && bd->rd->fHinges3d)
2516         CloseBoard3d(bd, bd->bd3d, bd->rd);
2517 #endif
2518     ProcessEvents();
2519 
2520     SoundWait();                /* Wait for sound to finish before final close */
2521 
2522     if (fInteractive)
2523         PortableSignalRestore(SIGINT, &shInterruptOld);
2524 
2525 #if defined(USE_GTK)
2526     if (fX) {
2527         stop_board_expose(bd);
2528         board_free_pixmaps(bd);
2529     }
2530 #if defined(USE_BOARD3D)
2531     if (fX && gtk_gl_init_success && bd)
2532         Tidy3dObjects(bd->bd3d, bd->rd);
2533 #endif
2534 #endif
2535 
2536 #if defined(HAVE_LIB_READLINE)
2537     write_history(gnubg_histfile);
2538 #endif                          /* HAVE_LIB_READLINE */
2539 
2540 #if defined(USE_GTK)
2541     if (gtk_main_level() == 1)
2542         gtk_main_quit();
2543     else
2544 #endif
2545     {
2546         Shutdown();
2547         exit(EXIT_SUCCESS);
2548     }
2549 }
2550 
2551 extern void
CommandNotImplemented(char * UNUSED (sz))2552 CommandNotImplemented(char *UNUSED(sz))
2553 {
2554     outputl(_("That command is not yet implemented."));
2555 }
2556 
2557 extern void
CommandQuit(char * UNUSED (sz))2558 CommandQuit(char *UNUSED(sz))
2559 {
2560     PromptForExit();
2561 }
2562 
2563 
2564 extern void
CommandRollout(char * sz)2565 CommandRollout(char *sz)
2566 {
2567     float arOutput[NUM_ROLLOUT_OUTPUTS];
2568     float arStdDev[NUM_ROLLOUT_OUTPUTS];
2569     rolloutstat arsStatistics[2];
2570     TanBoard anBoard;
2571     cubeinfo ci;
2572     char asz[1][40];
2573     void *p;
2574 
2575     if (CountTokens(sz) > 0) {
2576         outputerrf("%s", _("The rollout command takes no arguments and only rollouts the current position"));
2577         return;
2578     }
2579     if (ms.gs != GAME_PLAYING) {
2580         outputerrf("%s", _("No position specified and no game in progress."));
2581         return;
2582     }
2583 #if defined(USE_GTK)
2584     if (fX)
2585         GTKShowWarning(WARN_ROLLOUT, NULL);
2586 #endif
2587     sprintf(asz[0], _("Current Position"));
2588     memcpy(anBoard, msBoard(), sizeof(TanBoard));
2589     SetCubeInfo(&ci, ms.nCube, ms.fCubeOwner, ms.fMove, ms.nMatchTo, ms.anScore, ms.fCrawford, ms.fJacoby, nBeavers,
2590                 ms.bgv);
2591     RolloutProgressStart(&ci, 1, NULL, &rcRollout, asz, FALSE, &p);
2592     GeneralEvaluationR(arOutput, arStdDev, arsStatistics, (ConstTanBoard) anBoard, &ci, &rcRollout, RolloutProgress, p);
2593     RolloutProgressEnd(&p, FALSE);
2594 
2595 }
2596 
2597 static void
LoadCommands(FILE * pf,char * szFile)2598 LoadCommands(FILE * pf, char *szFile)
2599 {
2600     char sz[2048], *pch;
2601 
2602     outputpostpone();
2603 
2604     /* FIXME shouldn't restart sys calls on signals during this fgets */
2605     while (fgets(sz, sizeof(sz), pf) != NULL) {
2606 
2607         if ((pch = strchr(sz, '\n')))
2608             *pch = 0;
2609         if ((pch = strchr(sz, '\r')))
2610             *pch = 0;
2611 
2612         if (fInterrupt) {
2613             outputresume();
2614             return;
2615         }
2616 
2617         if (*sz == '#')         /* Comment */
2618             continue;
2619 
2620 #if defined(USE_PYTHON)
2621 
2622         if (!strcmp(sz, ">")) {
2623 
2624             /* Python escape. */
2625 
2626             /* Ideally we should be able to handle both > print 1+1 sys.exit() and > print 1+1
2627              * currently we only handle the latter... */
2628 
2629             outputerrf("%s", _("Only Python commands supported, not multiline code"));
2630 
2631             continue;
2632 
2633         }
2634 #endif                          /* USE_PYTHON */
2635 
2636         HandleCommand(sz, acTop);
2637 
2638         /* FIXME handle NextTurn events? */
2639     }
2640     if (ferror(pf)) {
2641         outputerr(szFile);
2642     }
2643 
2644     outputresume();
2645 }
2646 
2647 extern void
CommandLoadPython(char * sz)2648 CommandLoadPython(char *sz)
2649 {
2650     sz = NextToken(&sz);
2651 
2652     if (sz && *sz)
2653 #if defined(USE_PYTHON)
2654         LoadPythonFile(sz, FALSE);
2655 #else
2656         outputl(_("This build of GNU Backgammon does not support Python"));
2657 #endif
2658     else
2659         outputl(_("You must specify a file to load from."));
2660 }
2661 
2662 extern void
CommandLoadCommands(char * sz)2663 CommandLoadCommands(char *sz)
2664 {
2665     FILE *pf;
2666 
2667 #if defined(WIN32)
2668     /* Make sure unquoted filenames are quoted before processing
2669      * to allow proper support for filenames with spaces */
2670     char *szQuoted = NULL;
2671     char *szQuotedTemp = NULL;
2672     if (sz[0] != '"') {
2673         szQuoted = szQuotedTemp = g_strdup_printf("\"%s\"", sz);
2674         sz = NextToken(&szQuoted);
2675     } else
2676         sz = NextToken(&sz);
2677 #else
2678     sz = NextToken(&sz);
2679 #endif
2680 
2681     if (!sz || !*sz) {
2682         outputl(_("You must specify a file to load from."));
2683         return;
2684     }
2685 
2686     if ((pf = gnubg_g_fopen(sz, "r"))) {
2687         LoadCommands(pf, sz);
2688         fclose(pf);
2689     } else
2690         outputerr(sz);
2691 
2692 #if defined(WIN32)
2693     if (szQuotedTemp)
2694         g_free(szQuotedTemp);
2695 #endif
2696 }
2697 
2698 
2699 extern void
CommandCopy(char * UNUSED (sz))2700 CommandCopy(char *UNUSED(sz))
2701 {
2702     char *aps[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
2703     char szOut[2048];
2704     char szCube[32], szPlayer0[MAX_NAME_LEN + 3], szPlayer1[MAX_NAME_LEN + 3], szScore0[35], szScore1[35], szMatch[35];
2705     char szRolled[32];
2706     TanBoard anBoardTemp;
2707 
2708     aps[0] = szPlayer0;
2709     aps[6] = szPlayer1;
2710 
2711     sprintf(aps[1] = szScore0, ngettext("%d point", "%d points", ms.anScore[0]), ms.anScore[0]);
2712 
2713     sprintf(aps[5] = szScore1, ngettext("%d point", "%d points", ms.anScore[1]), ms.anScore[1]);
2714 
2715     if (ms.fDoubled) {
2716         aps[ms.fTurn ? 4 : 2] = szCube;
2717 
2718         sprintf(szPlayer0, "O: %s", ap[0].szName);
2719         sprintf(szPlayer1, "X: %s", ap[1].szName);
2720         sprintf(szCube, _("Cube offered at %d"), ms.nCube << 1);
2721     } else {
2722         sprintf(szPlayer0, "O: %s", ap[0].szName);
2723         sprintf(szPlayer1, "X: %s", ap[1].szName);
2724 
2725         aps[ms.fMove ? 4 : 2] = szRolled;
2726 
2727         if (ms.anDice[0])
2728             sprintf(szRolled, "%s %u%u", _("Rolled"), ms.anDice[0], ms.anDice[1]);
2729         else if (!GameStatus(msBoard(), ms.bgv))
2730             strcpy(szRolled, _("On roll"));
2731         else
2732             szRolled[0] = 0;
2733 
2734         if (ms.fCubeOwner < 0) {
2735             aps[3] = szCube;
2736 
2737             if (ms.nMatchTo)
2738                 if (ms.nMatchTo == 1)
2739                     sprintf(szCube, _("1 point match"));
2740                 else if (ms.fCrawford)
2741                     sprintf(szCube, _("%d point match (Crawford game)"), ms.nMatchTo);
2742                 else
2743                     sprintf(szCube, _("%d point match (Cube: %d)"), ms.nMatchTo, ms.nCube);
2744             else
2745                 sprintf(szCube, "(%s: %d)", _("Cube"), ms.nCube);
2746         } else {
2747             size_t cch = strlen(ap[ms.fCubeOwner].szName);
2748 
2749             if (cch > 20)
2750                 cch = 20;
2751 
2752             sprintf(szCube, "%c: %*s (%s: %d)", ms.fCubeOwner ? 'X' :
2753                     'O', (int) cch, ap[ms.fCubeOwner].szName, _("Cube"), ms.nCube);
2754 
2755             aps[ms.fCubeOwner ? 6 : 0] = szCube;
2756 
2757             if (ms.nMatchTo)
2758                 sprintf(aps[3] = szMatch, _("%d point match"), ms.nMatchTo);
2759         }
2760     }
2761 
2762     memcpy(anBoardTemp, msBoard(), sizeof(TanBoard));
2763 
2764     if (!ms.fMove)
2765         SwapSides(anBoardTemp);
2766 
2767     DrawBoard(szOut, (ConstTanBoard) anBoardTemp, ms.fMove, aps, MatchIDFromMatchState(&ms), anChequers[ms.bgv]);
2768 
2769     {
2770         unsigned int anPips[2];
2771         char szPipCount[32];
2772 
2773         PipCount((ConstTanBoard) anBoardTemp, anPips);
2774         sprintf(szPipCount, "Pip counts : O %u, X %u\n", anPips[0], anPips[1]);
2775 
2776         strcat(szOut, "                    ");
2777         strcat(szOut, szPipCount);
2778     }
2779 
2780     TextToClipboard(szOut);
2781 }
2782 
2783 static void
LoadRCFiles(void)2784 LoadRCFiles(void)
2785 {
2786     char *sz, *szz;
2787 
2788     loading_rc = TRUE;
2789     outputoff();
2790     sz = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
2791     szz = g_strdup_printf("\"%s\"", sz);
2792     if (g_file_test(sz, G_FILE_TEST_EXISTS))
2793         CommandLoadCommands(szz);
2794 
2795     UpdateSettings();
2796     g_free(sz);
2797     g_free(szz);
2798 
2799     sz = g_build_filename(szHomeDirectory, "gnubgrc", NULL);
2800     szz = g_strdup_printf("\"%s\"", sz);
2801     if (g_file_test(sz, G_FILE_TEST_EXISTS))
2802         CommandLoadCommands(szz);
2803     g_free(sz);
2804     g_free(szz);
2805 
2806     outputon();
2807     loading_rc = FALSE;
2808 }
2809 
2810 
2811 static void
SaveRNGSettings(FILE * pf,const char * sz,rng rngCurrent,rngcontext * rngctx)2812 SaveRNGSettings(FILE * pf, const char *sz, rng rngCurrent, rngcontext * rngctx)
2813 {
2814 
2815     switch (rngCurrent) {
2816     case RNG_BBS:
2817         fprintf(pf, "%s rng bbs\n", sz);        /* FIXME save modulus */
2818         break;
2819     case RNG_ISAAC:
2820         fprintf(pf, "%s rng isaac\n", sz);
2821         break;
2822     case RNG_MANUAL:
2823         fprintf(pf, "%s rng manual\n", sz);
2824         break;
2825     case RNG_MD5:
2826         fprintf(pf, "%s rng md5\n", sz);
2827         break;
2828     case RNG_MERSENNE:
2829         fprintf(pf, "%s rng mersenne\n", sz);
2830         break;
2831     case RNG_RANDOM_DOT_ORG:
2832         fprintf(pf, "%s rng random.org\n", sz);
2833         break;
2834     case RNG_FILE:
2835         fprintf(pf, "%s rng file \"%s\"\n", sz, GetDiceFileName(rngctx));
2836         break;
2837     default:
2838         break;
2839     }
2840 
2841 }
2842 
2843 
2844 static void
SaveMoveFilterSettings(FILE * pf,const char * sz,movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES])2845 SaveMoveFilterSettings(FILE * pf, const char *sz, movefilter aamf[MAX_FILTER_PLIES][MAX_FILTER_PLIES])
2846 {
2847 
2848     int i, j;
2849     gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
2850 
2851     for (i = 0; i < MAX_FILTER_PLIES; ++i)
2852         for (j = 0; j <= i; ++j) {
2853             fprintf(pf, "%s %d  %d  %d %d %s\n",
2854                     sz,
2855                     i + 1, j,
2856                     aamf[i][j].Accept,
2857                     aamf[i][j].Extra, g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%0.3g", aamf[i][j].Threshold));
2858         }
2859 }
2860 
2861 
2862 
2863 
2864 static void
SaveEvalSettings(FILE * pf,const char * sz,evalcontext * pec)2865 SaveEvalSettings(FILE * pf, const char *sz, evalcontext * pec)
2866 {
2867 
2868     gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
2869     gchar *szNoise = g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", pec->rNoise);
2870     fprintf(pf, "%s plies %u\n"
2871             "%s prune %s\n"
2872             "%s cubeful %s\n"
2873             "%s noise %s\n"
2874             "%s deterministic %s\n",
2875             sz, pec->nPlies,
2876             sz, pec->fUsePrune ? "on" : "off",
2877             sz, pec->fCubeful ? "on" : "off", sz, szNoise, sz, pec->fDeterministic ? "on" : "off");
2878 }
2879 
2880 
2881 extern void
SaveRolloutSettings(FILE * pf,const char * sz,rolloutcontext * prc)2882 SaveRolloutSettings(FILE * pf, const char *sz, rolloutcontext * prc)
2883 {
2884 
2885     char *pch;
2886     int i;                      /* flags and stuff */
2887     gchar szTemp1[G_ASCII_DTOSTR_BUF_SIZE];
2888     gchar szTemp2[G_ASCII_DTOSTR_BUF_SIZE];
2889 
2890     g_ascii_formatd(szTemp1, G_ASCII_DTOSTR_BUF_SIZE, "%05.4f", prc->rStdLimit);
2891     g_ascii_formatd(szTemp2, G_ASCII_DTOSTR_BUF_SIZE, "%05.4f", prc->rJsdLimit);
2892 
2893     fprintf(pf,
2894             "%s cubeful %s\n"
2895             "%s varredn %s\n"
2896             "%s quasirandom %s\n"
2897             "%s initial %s\n"
2898             "%s truncation enable %s\n"
2899             "%s truncation plies %u\n"
2900             "%s bearofftruncation exact %s\n"
2901             "%s bearofftruncation onesided %s\n"
2902             "%s later enable %s\n"
2903             "%s later plies %u\n"
2904             "%s trials %u\n"
2905             "%s cube-equal-chequer %s\n"
2906             "%s players-are-same %s\n"
2907             "%s truncate-equal-player0 %s\n"
2908             "%s limit enable %s\n"
2909             "%s limit minimumgames %u\n"
2910             "%s limit maxerror %s\n"
2911             "%s jsd stop %s\n"
2912             "%s jsd minimumgames %u\n"
2913             "%s jsd limit %s\n",
2914             sz, prc->fCubeful ? "on" : "off",
2915             sz, prc->fVarRedn ? "on" : "off",
2916             sz, prc->fRotate ? "on" : "off",
2917             sz, prc->fInitial ? "on" : "off",
2918             sz, prc->fDoTruncate ? "on" : "off",
2919             sz, prc->nTruncate,
2920             sz, prc->fTruncBearoff2 ? "on" : "off",
2921             sz, prc->fTruncBearoffOS ? "on" : "off",
2922             sz, prc->fLateEvals ? "on" : "off",
2923             sz, prc->nLate,
2924             sz, prc->nTrials,
2925             sz, fCubeEqualChequer ? "on" : "off",
2926             sz, fPlayersAreSame ? "on" : "off",
2927             sz, fTruncEqualPlayer0 ? "on" : "off",
2928             sz, prc->fStopOnSTD ? "on" : "off",
2929             sz, prc->nMinimumGames,
2930             sz, szTemp1, sz, prc->fStopOnJsd ? "on" : "off", sz, prc->nMinimumJsdGames, sz, szTemp2);
2931 
2932     SaveRNGSettings(pf, sz, prc->rngRollout, rngctxRollout);
2933 
2934     /* chequer play and cube decision evalcontexts */
2935 
2936     pch = malloc(strlen(sz) + 50);
2937 
2938     strcpy(pch, sz);
2939 
2940     for (i = 0; i < 2; i++) {
2941 
2942         sprintf(pch, "%s player %i chequerplay", sz, i);
2943         SaveEvalSettings(pf, pch, &prc->aecChequer[i]);
2944 
2945         sprintf(pch, "%s player %i cubedecision", sz, i);
2946         SaveEvalSettings(pf, pch, &prc->aecCube[i]);
2947 
2948         sprintf(pch, "%s player %i movefilter", sz, i);
2949         SaveMoveFilterSettings(pf, pch, prc->aaamfChequer[i]);
2950 
2951     }
2952 
2953     for (i = 0; i < 2; i++) {
2954 
2955         sprintf(pch, "%s later player %i chequerplay", sz, i);
2956         SaveEvalSettings(pf, pch, &prc->aecChequerLate[i]);
2957 
2958         sprintf(pch, "%s later player %i cubedecision", sz, i);
2959         SaveEvalSettings(pf, pch, &prc->aecCubeLate[i]);
2960 
2961         sprintf(pch, "%s later player %i movefilter", sz, i);
2962         SaveMoveFilterSettings(pf, pch, prc->aaamfLate[i]);
2963 
2964     }
2965 
2966     sprintf(pch, "%s truncation cubedecision", sz);
2967     SaveEvalSettings(pf, pch, &prc->aecCubeTrunc);
2968     sprintf(pch, "%s truncation chequerplay", sz);
2969     SaveEvalSettings(pf, pch, &prc->aecChequerTrunc);
2970 
2971     free(pch);
2972 
2973 }
2974 
2975 static void
SaveEvalSetupSettings(FILE * pf,const char * sz,evalsetup * pes)2976 SaveEvalSetupSettings(FILE * pf, const char *sz, evalsetup * pes)
2977 {
2978 
2979     char szTemp[1024];
2980 
2981     switch (pes->et) {
2982     case EVAL_EVAL:
2983         fprintf(pf, "%s type evaluation\n", sz);
2984         break;
2985     case EVAL_ROLLOUT:
2986         fprintf(pf, "%s type rollout\n", sz);
2987         break;
2988     default:
2989         break;
2990     }
2991 
2992     strcpy(szTemp, sz);
2993     SaveEvalSettings(pf, strcat(szTemp, " evaluation"), &pes->ec);
2994 
2995     strcpy(szTemp, sz);
2996     SaveRolloutSettings(pf, strcat(szTemp, " rollout"), &pes->rc);
2997 
2998 }
2999 
3000 static void
SaveAnalysisSettings(FILE * pf)3001 SaveAnalysisSettings(FILE * pf)
3002 {
3003     gchar aszThr[7][G_ASCII_DTOSTR_BUF_SIZE];
3004     g_ascii_formatd(aszThr[0], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arSkillLevel[SKILL_BAD]);
3005     g_ascii_formatd(aszThr[1], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arSkillLevel[SKILL_DOUBTFUL]);
3006     g_ascii_formatd(aszThr[2], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_GOOD]);
3007     g_ascii_formatd(aszThr[3], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_BAD]);
3008     g_ascii_formatd(aszThr[4], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arSkillLevel[SKILL_VERYBAD]);
3009     g_ascii_formatd(aszThr[5], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_VERYGOOD]);
3010     g_ascii_formatd(aszThr[6], G_ASCII_DTOSTR_BUF_SIZE, "%0.3f", arLuckLevel[LUCK_VERYBAD]);
3011 
3012     SaveEvalSetupSettings(pf, "set analysis chequerplay", &esAnalysisChequer);
3013     SaveEvalSetupSettings(pf, "set analysis cubedecision", &esAnalysisCube);
3014     SaveMoveFilterSettings(pf, "set analysis movefilter", aamfAnalysis);
3015     SaveEvalSettings(pf, "set analysis luckanalysis", &ecLuck);
3016     fprintf(pf, "set analysis threshold bad %s\n", aszThr[0]);
3017     fprintf(pf, "set analysis threshold doubtful %s\n", aszThr[1]);
3018     fprintf(pf, "set analysis threshold lucky %s\n", aszThr[2]);
3019     fprintf(pf, "set analysis threshold unlucky %s\n", aszThr[3]);
3020     fprintf(pf, "set analysis threshold verybad %s\n", aszThr[4]);
3021     fprintf(pf, "set analysis threshold verylucky %s\n", aszThr[5]);
3022     fprintf(pf, "set analysis threshold veryunlucky %s\n", aszThr[6]);
3023     fprintf(pf, "set analysis cube %s\n", fAnalyseCube ? "on" : "off");
3024     fprintf(pf, "set analysis luck %s\n", fAnalyseDice ? "on" : "off");
3025     fprintf(pf, "set analysis moves %s\n", fAnalyseMove ? "on" : "off");
3026     fprintf(pf, "set analysis player 0 analyse %s\n", afAnalysePlayers[0] ? "yes" : "no");
3027     fprintf(pf, "set analysis player 1 analyse %s\n", afAnalysePlayers[1] ? "yes" : "no");
3028 }
3029 
3030 static void
SaveImportExportSettings(FILE * pf)3031 SaveImportExportSettings(FILE * pf)
3032 {
3033     int i;
3034     if (default_import_folder && *default_import_folder)
3035         fprintf(pf, "set import folder \"%s\"\n", default_import_folder);
3036     if (default_export_folder && *default_export_folder)
3037         fprintf(pf, "set export folder \"%s\"\n", default_export_folder);
3038     if (default_sgf_folder && *default_sgf_folder)
3039         fprintf(pf, "set sgf folder \"%s\"\n", default_sgf_folder);
3040 
3041     fprintf(pf, "set export include annotations %s\n", exsExport.fIncludeAnnotation ? "yes" : "no");
3042     fprintf(pf, "set export include analysis %s\n", exsExport.fIncludeAnalysis ? "yes" : "no");
3043     fprintf(pf, "set export include statistics %s\n", exsExport.fIncludeStatistics ? "yes" : "no");
3044     fprintf(pf, "set export include matchinfo %s\n", exsExport.fIncludeMatchInfo ? "yes" : "no");
3045     fprintf(pf, "set export show board %d\n", exsExport.fDisplayBoard);
3046 
3047     if (exsExport.fSide == 3)
3048         fprintf(pf, "set export show player both\n");
3049     else if (exsExport.fSide)
3050         fprintf(pf, "set export show player %d\n", exsExport.fSide - 1);
3051 
3052     fprintf(pf, "set export move number %u\n", exsExport.nMoves);
3053     fprintf(pf, "set export moves parameters evaluation %s\n", exsExport.afMovesParameters[0] ? "yes" : "no");
3054     fprintf(pf, "set export moves parameters rollout %s\n", exsExport.afMovesParameters[1] ? "yes" : "no");
3055     fprintf(pf, "set export moves probabilities %s\n", exsExport.fMovesDetailProb ? "yes" : "no");
3056 
3057     for (i = 0; i < N_SKILLS; i++) {
3058         if (i == SKILL_NONE)
3059             fprintf(pf, "set export moves display unmarked %s\n", exsExport.afMovesDisplay[i] ? "yes" : "no");
3060         else
3061             fprintf(pf, "set export moves display %s %s\n",
3062                     aszSkillTypeCommand[i], exsExport.afMovesDisplay[i] ? "yes" : "no");
3063     }
3064 
3065     fprintf(pf, "set export cube parameters evaluation %s\n", exsExport.afCubeParameters[0] ? "yes" : "no");
3066     fprintf(pf, "set export cube parameters rollout %s\n", exsExport.afCubeParameters[1] ? "yes" : "no");
3067     fprintf(pf, "set export cube probabilities %s\n", exsExport.fCubeDetailProb ? "yes" : "no");
3068 
3069     for (i = 0; i < N_SKILLS; i++) {
3070         if (i == SKILL_NONE)
3071             fprintf(pf, "set export cube display unmarked %s\n", exsExport.afCubeDisplay[i] ? "yes" : "no");
3072         else
3073             fprintf(pf, "set export cube display %s %s\n",
3074                     aszSkillTypeCommand[i], exsExport.afCubeDisplay[i] ? "yes" : "no");
3075     }
3076 
3077     fprintf(pf, "set export cube display actual %s\n", exsExport.afCubeDisplay[EXPORT_CUBE_ACTUAL] ? "yes" : "no");
3078     fprintf(pf, "set export cube display missed %s\n", exsExport.afCubeDisplay[EXPORT_CUBE_MISSED] ? "yes" : "no");
3079     fprintf(pf, "set export cube display close %s\n", exsExport.afCubeDisplay[EXPORT_CUBE_CLOSE] ? "yes" : "no");
3080     fprintf(pf, "set export html pictureurl \"%s\"\n", exsExport.szHTMLPictureURL);
3081     fprintf(pf, "set export html type \"%s\"\n", aszHTMLExportType[exsExport.het]);
3082     fprintf(pf, "set export html css %s\n", aszHTMLExportCSSCommand[exsExport.hecss]);
3083     fprintf(pf, "set export png size %d\n", exsExport.nPNGSize);
3084     fprintf(pf, "set export html size %d\n", exsExport.nHtmlSize);
3085 
3086 }
3087 
3088 #if defined(USE_GTK)
3089 static void
SaveGUISettings(FILE * pf)3090 SaveGUISettings(FILE * pf)
3091 {
3092     const char *aszAnimation[] = { "none", "blink", "slide" };
3093     const char *aszShowPips[N_GUI_SHOW_PIPS] = { "none", "pips", "epc", "wastage" };
3094     int dummy;
3095 
3096     SaveWindowSettings(pf);
3097     WriteWarnings(pf);
3098     if (fX)
3099         GTKSaveSettings();
3100 
3101     fprintf(pf, "set fullscreen %s\n", fFullScreen ? "on" : "off");
3102     if (fFullScreen) {
3103         GetFullscreenWindowSettings(&dummy, &fShowIDs, &dummy);
3104         fprintf(pf, "set gui showids %s\n", fShowIDs ? "on" : "off");
3105         fShowIDs = FALSE;
3106     } else
3107         fprintf(pf, "set gui showids %s\n", fShowIDs ? "on" : "off");
3108 
3109     fprintf(pf, "set gui animation %s\n", aszAnimation[animGUI]);
3110     fprintf(pf, "set gui animation speed %u\n", nGUIAnimSpeed);
3111     fprintf(pf, "set gui beep %s\n", fGUIBeep ? "on" : "off");
3112     fprintf(pf, "set gui dicearea %s\n", GetMainAppearance()->fDiceArea ? "on" : "off");
3113     fprintf(pf, "set gui highdiefirst %s\n", fGUIHighDieFirst ? "on" : "off");
3114     fprintf(pf, "set gui illegal %s\n", fGUIIllegal ? "on" : "off");
3115     fprintf(pf, "set gui showpips %s\n", aszShowPips[gui_show_pips]);
3116     fprintf(pf, "set gui dragtargethelp %s\n", fGUIDragTargetHelp ? "on" : "off");
3117     fprintf(pf, "set gui usestatspanel %s\n", fGUIUseStatsPanel ? "on" : "off");
3118     fprintf(pf, "set gui movelistdetail %s\n", showMoveListDetail ? "on" : "off");
3119     fprintf(pf, "set gui grayedit %s\n", fGUIGrayEdit ? "on" : "off");
3120     fprintf(pf, "set gui windowpositions %s\n", fGUISetWindowPos ? "on" : "off");
3121 
3122     fprintf(pf, "set styledgamelist %s\n", fStyledGamelist ? "on" : "off");
3123     fprintf(pf, "set delay %u\n", nDelay);
3124 
3125     fprintf(pf, "set toolbar %d\n", nToolbarStyle);
3126     if (!fToolbarShowing)
3127         fputs("set toolbar off\n", pf);
3128 
3129 #if defined(USE_BOARD3D)
3130     if (fSync != -1)
3131         fprintf(pf, "set vsync3d %s\n", fSync ? "yes" : "no");
3132 #endif
3133 }
3134 #endif
3135 
3136 static void
SaveSoundSettings(FILE * pf)3137 SaveSoundSettings(FILE * pf)
3138 {
3139     gnubgsound i;
3140     fprintf(pf, "set sound enable %s\n", fSound ? "yes" : "no");
3141     fprintf(pf, "set sound system command %s\n", sound_get_command());
3142 
3143     for (i = (gnubgsound) 0; i < NUM_SOUNDS; ++i) {
3144         char *file = GetSoundFile(i);
3145         fprintf(pf, "set sound sound %s \"%s\"\n", sound_command[i], file);
3146         g_free(file);
3147     }
3148 }
3149 
3150 static void
SavePlayerSettings(FILE * pf)3151 SavePlayerSettings(FILE * pf)
3152 {
3153     int i;
3154     char szTemp[4096];
3155 
3156     fprintf(pf, "set defaultnames \"%s\" \"%s\"\n", default_names[0], default_names[1]);
3157     if (strlen(player1aliases) > 0)
3158         fprintf(pf, "set aliases %s\n", player1aliases);
3159 
3160     for (i = 0; i < 2; i++) {
3161         fprintf(pf, "set player %d name %s\n", i, ap[i].szName);
3162 
3163         switch (ap[i].pt) {
3164         case PLAYER_GNU:
3165             fprintf(pf, "set player %d gnubg\n", i);
3166             sprintf(szTemp, "set player %d chequerplay", i);
3167             SaveEvalSetupSettings(pf, szTemp, &ap[i].esChequer);
3168             sprintf(szTemp, "set player %d cubedecision", i);
3169             SaveEvalSetupSettings(pf, szTemp, &ap[i].esCube);
3170             sprintf(szTemp, "set player %d movefilter", i);
3171             SaveMoveFilterSettings(pf, szTemp, ap[i].aamf);
3172             break;
3173 
3174         case PLAYER_HUMAN:
3175             fprintf(pf, "set player %d human\n", i);
3176             break;
3177 
3178         case PLAYER_EXTERNAL:
3179             /* don't save external players */
3180             break;
3181         }
3182     }
3183     fprintf(pf, "set cheat enable %s\n", fCheat ? "on" : "off");
3184     for (i = 0; i < 2; ++i)
3185         fprintf(pf, "set cheat player %d roll %u\n", i, afCheatRoll[i] + 1);
3186 
3187 }
3188 
3189 static void
SavePlayingSettings(FILE * pf)3190 SavePlayingSettings(FILE * pf)
3191 {
3192     fprintf(pf, "set automatic bearoff %s\n", fAutoBearoff ? "on" : "off");
3193     fprintf(pf, "set automatic crawford %s\n", fAutoCrawford ? "on" : "off");
3194     fprintf(pf, "set automatic game %s\n", fAutoGame ? "on" : "off");
3195     fprintf(pf, "set automatic move %s\n", fAutoMove ? "on" : "off");
3196     fprintf(pf, "set automatic roll %s\n", fAutoRoll ? "on" : "off");
3197 }
3198 
3199 static void
SaveRuleSettings(FILE * pf)3200 SaveRuleSettings(FILE * pf)
3201 {
3202     fprintf(pf, "set automatic doubles %u\n", cAutoDoubles);
3203     fprintf(pf, "set beavers %u\n", nBeavers);
3204     fprintf(pf, "set jacoby %s\n", fJacoby ? "on" : "off");
3205     fprintf(pf, "set matchlength %u\n", nDefaultLength);
3206     fprintf(pf, "set variation %s\n", aszVariationCommands[bgvDefault]);
3207 }
3208 
3209 static void
SaveEvaluationSettings(FILE * pf)3210 SaveEvaluationSettings(FILE * pf)
3211 {
3212     fprintf(pf, "set eval sameasanalysis %s\n", fEvalSameAsAnalysis ? "on" : "off");
3213     SaveEvalSetupSettings(pf, "set evaluation chequerplay", &esEvalChequer);
3214     SaveEvalSetupSettings(pf, "set evaluation cubedecision", &esEvalCube);
3215     SaveMoveFilterSettings(pf, "set evaluation movefilter", aamfEval);
3216     fprintf(pf, "set cache %u\n", GetEvalCacheEntries());
3217     fprintf(pf, "set matchequitytable \"%s\"\n", miCurrent.szFileName);
3218     fprintf(pf, "set invert matchequitytable %s\n", fInvertMET ? "on" : "off");
3219 #if defined(USE_MULTITHREAD)
3220     fprintf(pf, "set threads %u\n", MT_GetNumThreads());
3221 #endif
3222 }
3223 
3224 static void
SaveAutoSaveSettings(FILE * pf)3225 SaveAutoSaveSettings(FILE * pf)
3226 {
3227     fprintf(pf, "set autosave time %d\n", nAutoSaveTime);
3228     fprintf(pf, "set autosave rollout %s\n", fAutoSaveRollout ? "on" : "off");
3229     fprintf(pf, "set autosave analysis %s\n", fAutoSaveAnalysis ? "on" : "off");
3230     fprintf(pf, "set autosave confirm %s\n", fAutoSaveConfirmDelete ? "on" : "off");
3231 }
3232 
3233 static void
SaveMiscSettings(FILE * pf)3234 SaveMiscSettings(FILE * pf)
3235 {
3236     gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
3237     fprintf(pf, "set tutor mode %s\n", fTutor ? "on" : "off");
3238     fprintf(pf, "set tutor cube %s\n", fTutorCube ? "on" : "off");
3239     fprintf(pf, "set tutor chequer %s\n", fTutorChequer ? "on" : "off");
3240     fprintf(pf, "set tutor skill ");
3241     if (TutorSkill == SKILL_VERYBAD)
3242         fprintf(pf, "very bad\n");
3243     else if (TutorSkill == SKILL_BAD)
3244         fprintf(pf, "bad\n");
3245     else
3246         fprintf(pf, "doubtful\n");
3247 
3248     fprintf(pf, "set clockwise %s\n", fClockwise ? "on" : "off");
3249     fprintf(pf, "set confirm new %s\n", fConfirmNew ? "on" : "off");
3250     fprintf(pf, "set confirm save %s\n", fConfirmSave ? "on" : "off");
3251     fprintf(pf, "set cube use %s\n", fCubeUse ? "on" : "off");
3252     fprintf(pf, "set display %s\n", fDisplay ? "on" : "off");
3253 
3254     fprintf(pf, "set confirm default ");
3255     if (nConfirmDefault == 1)
3256         fprintf(pf, "yes\n");
3257     else if (nConfirmDefault == 0)
3258         fprintf(pf, "no\n");
3259     else
3260         fprintf(pf, "ask\n");
3261 
3262     fprintf(pf, "set gotofirstgame %s\n", fGotoFirstGame ? "on" : "off");
3263     fprintf(pf, "set output matchpc %s\n", fOutputMatchPC ? "on" : "off");
3264     fprintf(pf, "set output mwc %s\n", fOutputMWC ? "on" : "off");
3265     fprintf(pf, "set output rawboard %s\n", fOutputRawboard ? "on" : "off");
3266     fprintf(pf, "set output winpc %s\n", fOutputWinPC ? "on" : "off");
3267     fprintf(pf, "set output digits %d\n", fOutputDigits);
3268     fprintf(pf, "set output errorratefactor %s\n",
3269             g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%f", rErrorRateFactor));
3270     fprintf(pf, "set prompt %s\n", szPrompt);
3271     fprintf(pf, "set browser \"%s\"\n", get_web_browser());
3272     fprintf(pf, "set priority nice %d\n", nThreadPriority);
3273     fprintf(pf, "set ratingoffset %s\n", g_ascii_formatd(buf, G_ASCII_DTOSTR_BUF_SIZE, "%f", rRatingOffset));
3274 }
3275 
3276 extern void
CommandSaveSettings(char * szParam)3277 CommandSaveSettings(char *szParam)
3278 {
3279     FILE *pf;
3280     char *szFile;
3281 
3282     szParam = NextToken(&szParam);
3283 
3284     /* If we were started without a configuration file, don't
3285      * save anything since this will overwrite an existing file
3286      * with defaults */
3287     if (fNoRC)
3288         return;
3289 
3290     if (!szParam || !*szParam) {
3291         /* no filename parameter given -- save to default location */
3292         szFile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
3293     } else
3294         szFile = g_strdup(szParam);
3295 
3296     if (!strcmp(szFile, "-"))
3297         pf = stdout;
3298     else
3299         pf = gnubg_g_fopen(szFile, "w");
3300 
3301     if (!pf) {
3302         outputerr(szFile);
3303         g_free(szFile);
3304         return;
3305     }
3306 #if defined(USE_GTK)
3307     /* the following may set errno because of a gtk bug so we do it
3308      * first and reset errno afterwards */
3309     if (fX)
3310         RefreshGeometries();
3311 #endif
3312 
3313     errno = 0;
3314 
3315     fprintf(pf,
3316             "#\n"
3317             "# GNU Backgammon command file\n"
3318             "#   generated by %s\n"
3319             "#\n"
3320             "# WARNING: The file 'gnubgautorc' is automatically "
3321             "generated the first time\n"
3322             "# settings are changed and will be overwritten with "
3323             "every subsequent change.\n"
3324             "# If you want to add startup commands manually, you should "
3325             "use 'gnubgrc'\n" "# instead.\n" "\n", VERSION_STRING);
3326 
3327     /* language preference */
3328     fprintf(pf, "set lang %s\n", szLang);
3329 
3330 #if defined(USE_GTK)
3331     SaveGUISettings(pf);
3332 #endif
3333     SaveAnalysisSettings(pf);
3334     SaveRenderingSettings(pf);
3335     SavePlayingSettings(pf);
3336     SaveRuleSettings(pf);
3337     SaveEvaluationSettings(pf);
3338     SavePlayerSettings(pf);
3339     SaveRNGSettings(pf, "set", rngCurrent, rngctxCurrent);
3340     SaveRolloutSettings(pf, "set rollout", &rcRollout);
3341     SaveImportExportSettings(pf);
3342     SaveSoundSettings(pf);
3343     RelationalSaveSettings(pf);
3344 
3345     SaveMiscSettings(pf);
3346     SaveAutoSaveSettings(pf);
3347     if (pf != stdout)
3348         fclose(pf);
3349 
3350     if (errno)
3351         outputerr(szFile);
3352     else {
3353         outputf(_("Settings saved to %s."), (!strcmp(szFile, "-")) ? _("standard output stream") : szFile);
3354         output("\n");
3355         if (cOutputPostponed) {
3356             outputresume();
3357             outputx();
3358             outputpostpone();
3359         }
3360     }
3361     g_free(szFile);
3362 
3363 }
3364 
3365 #if defined(HAVE_LIB_READLINE)
3366 static command *pcCompleteContext;
3367 
3368 static char *
NullGenerator(const char * UNUSED (sz),int UNUSED (nState))3369 NullGenerator(const char *UNUSED(sz), int UNUSED(nState))
3370 {
3371     return NULL;
3372 }
3373 
3374 static char *
GenerateKeywords(const char * sz,int nState)3375 GenerateKeywords(const char *sz, int nState)
3376 {
3377     static size_t cch;
3378     static command *pc;
3379     char *szDup;
3380 
3381     if (!nState) {
3382         cch = strlen(sz);
3383         pc = pcCompleteContext;
3384     }
3385 
3386     while (pc && pc->sz) {
3387         if (!StrNCaseCmp(sz, pc->sz, cch) && pc->szHelp) {
3388             if (!(szDup = malloc(strlen(pc->sz) + 1)))
3389                 return NULL;
3390 
3391             strcpy(szDup, pc->sz);
3392 
3393             pc++;
3394 
3395             return szDup;
3396         }
3397 
3398         pc++;
3399     }
3400 
3401     return NULL;
3402 }
3403 
3404 static char *
ERCompletion(const char * sz,int nState)3405 ERCompletion(const char *sz, int nState)
3406 {
3407     static int i;
3408     static size_t cch;
3409     const char *pch;
3410     char *szDup;
3411 
3412     if (!nState) {
3413         cch = strlen(sz);
3414         i = 0;
3415     }
3416 
3417     while (i < 2) {
3418         pch = i++ ? "rollout" : "evaluation";
3419 
3420         if (!StrNCaseCmp(sz, pch, cch)) {
3421             if (!(szDup = malloc(strlen(pch) + 1)))
3422                 return NULL;
3423 
3424             return strcpy(szDup, pch);
3425         }
3426         ++i;
3427     }
3428 
3429     return NULL;
3430 }
3431 
3432 static char *
OnOffCompletion(const char * sz,int nState)3433 OnOffCompletion(const char *sz, int nState)
3434 {
3435     static unsigned int i;
3436     static size_t cch;
3437     static const char *asz[] = { "false", "no", "off", "on", "true", "yes" };
3438     const char *pch;
3439     char *szDup;
3440 
3441     if (!nState) {
3442         cch = strlen(sz);
3443         i = 0;
3444     }
3445 
3446     while (i < sizeof(asz) / sizeof(asz[0])) {
3447         pch = asz[i++];
3448 
3449         if (!StrNCaseCmp(sz, pch, cch)) {
3450             if (!(szDup = malloc(strlen(pch) + 1)))
3451                 return NULL;
3452 
3453             return strcpy(szDup, pch);
3454         }
3455     }
3456 
3457     return NULL;
3458 }
3459 
3460 static char *
PlayerCompletionGen(const char * sz,int nState,int fBoth)3461 PlayerCompletionGen(const char *sz, int nState, int fBoth)
3462 {
3463     static int i;
3464     static size_t cch;
3465     const char *pch;
3466     char *szDup;
3467 
3468     if (!nState) {
3469         cch = strlen(sz);
3470         i = 0;
3471     }
3472 
3473     while (i < (fBoth ? 5 : 4)) {
3474         switch (i) {
3475         case 0:
3476             pch = "0";
3477             break;
3478         case 1:
3479             pch = "1";
3480             break;
3481         case 2:
3482             pch = ap[0].szName;
3483             break;
3484         case 3:
3485             pch = ap[1].szName;
3486             break;
3487         case 4:
3488             pch = "both";
3489             break;
3490         default:
3491             abort();
3492         }
3493 
3494         i++;
3495 
3496         if (!StrNCaseCmp(sz, pch, cch)) {
3497             if (!(szDup = malloc(strlen(pch) + 1)))
3498                 return NULL;
3499 
3500             return strcpy(szDup, pch);
3501         }
3502     }
3503 
3504     return NULL;
3505 }
3506 
3507 static char *
PlayerCompletion(const char * sz,int nState)3508 PlayerCompletion(const char *sz, int nState)
3509 {
3510     return PlayerCompletionGen(sz, nState, FALSE);
3511 }
3512 
3513 static char *
PlayerCompletionBoth(const char * sz,int nState)3514 PlayerCompletionBoth(const char *sz, int nState)
3515 {
3516     return PlayerCompletionGen(sz, nState, TRUE);
3517 }
3518 
3519 
3520 static command *
FindContext(command * pc,char * szOrig,int ich)3521 FindContext(command * pc, char *szOrig, int ich)
3522 {
3523     char *sz = (char *) g_alloca(strlen(szOrig) * sizeof(char) + 1);
3524     char *pch, *pchCurrent;
3525     command *pcResume = NULL;
3526 
3527     pch = strcpy(sz, szOrig);
3528     pch[ich] = 0;
3529 
3530     do {
3531         if (!(pchCurrent = NextToken(&pch)))
3532             /* no command */
3533             return pc;
3534 
3535         if (!pch)
3536             /* incomplete command */
3537             return pc;
3538 
3539         if (pcResume) {
3540             pc = pcResume;
3541             pcResume = NULL;
3542             continue;
3543         }
3544 
3545         while (pc && pc->sz) {
3546             if (!StrNCaseCmp(pchCurrent, pc->sz, strlen(pchCurrent))) {
3547                 pc = pc->pc;
3548 
3549                 if (pc == acSetPlayer || pc == acSetRolloutPlayer ||
3550                     pc == acSetRolloutLatePlayer || pc == acSetAnalysisPlayer || pc == acSetCheatPlayer) {
3551                     pcResume = pc;
3552                     pc = &cPlayerBoth;
3553                 }
3554 
3555                 break;
3556             }
3557 
3558             pc++;
3559         }
3560     } while (pcResume || (pc && pc->sz));
3561 
3562     if (pc && pc->pc) {
3563         /* dummy command for parameter completion */
3564         if (!NextToken(&pch))
3565             return pc;
3566     }
3567 
3568     /* the command is already complete */
3569     return NULL;
3570 }
3571 
3572 static char **
CompleteKeyword(const char * szText,int iStart,int UNUSED (iEnd))3573 CompleteKeyword(const char *szText, int iStart, int UNUSED(iEnd))
3574 {
3575 
3576     if (fReadingOther)
3577         return rl_completion_matches(szText, NullGenerator);
3578 
3579     pcCompleteContext = FindContext(acTop, rl_line_buffer, iStart);
3580 
3581     if (!pcCompleteContext)
3582         return NULL;
3583 
3584     if (pcCompleteContext == &cER)
3585         return rl_completion_matches(szText, ERCompletion);
3586     else if (pcCompleteContext == &cFilename) {
3587         rl_filename_completion_desired = TRUE;
3588         return rl_completion_matches(szText, rl_filename_completion_function);
3589     } else if (pcCompleteContext == &cOnOff)
3590         return rl_completion_matches(szText, OnOffCompletion);
3591     else if (pcCompleteContext == &cPlayer)
3592         return rl_completion_matches(szText, PlayerCompletion);
3593     else if (pcCompleteContext == &cPlayerBoth)
3594         return rl_completion_matches(szText, PlayerCompletionBoth);
3595     else
3596         return rl_completion_matches(szText, GenerateKeywords);
3597 }
3598 #endif
3599 
3600 extern void
Prompt(void)3601 Prompt(void)
3602 {
3603 
3604 #if defined(HAVE_LIB_READLINE)
3605     if (!fInteractive || !isatty(STDIN_FILENO))
3606         return;
3607 #endif
3608 
3609     g_print("%s", FormatPrompt());
3610     fflush(stdout);
3611 }
3612 
3613 #if defined(HAVE_LIB_READLINE)
3614 static char *
locale_from_utf8(const char * sz)3615 locale_from_utf8(const char *sz)
3616 {
3617     char *ret;
3618     GError *error = NULL;
3619     g_assert(sz);
3620     ret = g_locale_from_utf8(sz, strlen(sz), NULL, NULL, &error);
3621     if (error) {
3622         g_print("locale_from_utf8 failed: %s\n", error->message);
3623         g_error_free(error);
3624         ret = g_strdup(sz);
3625     }
3626     return ret;
3627 }
3628 #endif
3629 
3630 static char *
locale_to_utf8(const char * sz)3631 locale_to_utf8(const char *sz)
3632 {
3633     char *ret;
3634     GError *error = NULL;
3635     g_assert(sz);
3636     ret = g_locale_to_utf8(sz, strlen(sz), NULL, NULL, &error);
3637     if (error) {
3638         g_print("locale_to_utf8 failed: %s\n", error->message);
3639         g_error_free(error);
3640         ret = g_strdup(sz);
3641     }
3642     return ret;
3643 }
3644 
3645 #if defined(USE_GTK)
3646 #if defined(HAVE_LIB_READLINE)
3647 extern void
ProcessInput(char * sz)3648 ProcessInput(char *sz)
3649 {
3650 
3651     char *pchExpanded;
3652 
3653     rl_callback_handler_remove();
3654     fReadingCommand = FALSE;
3655 
3656     if (!sz) {
3657         outputc('\n');
3658         PromptForExit();
3659     } else {
3660 
3661         fInterrupt = FALSE;
3662 
3663         /* expand history */
3664 
3665         history_expand(sz, &pchExpanded);
3666 
3667         if (*pchExpanded)
3668             add_history(pchExpanded);
3669 
3670         if (fX)
3671             GTKDisallowStdin();
3672 
3673         HandleCommand(pchExpanded, acTop);
3674         free(pchExpanded);
3675     }
3676 
3677     if (fX)
3678         GTKAllowStdin();
3679 
3680     ResetInterrupt();
3681 
3682 
3683     /* Recalculate prompt -- if we call nothing, then readline will
3684      * redisplay the old prompt.  This isn't what we want: we either
3685      * want no prompt at all, yet (if NextTurn is going to be called),
3686      * or if we do want to prompt immediately, we recalculate it in
3687      * case the information in the old one is out of date. */
3688     if (nNextTurn)
3689         fNeedPrompt = TRUE;
3690     else {
3691         char *sz = locale_from_utf8(FormatPrompt());
3692         rl_callback_handler_install(sz, ProcessInput);
3693         g_free(sz);
3694         fReadingCommand = TRUE;
3695     }
3696 }
3697 
3698 #endif
3699 
3700 /* Handle a command as if it had been typed by the user. */
3701 extern void
UserCommand(const char * szCommand)3702 UserCommand(const char *szCommand)
3703 {
3704     char *sz;
3705 
3706     g_return_if_fail(szCommand);
3707     g_return_if_fail(*szCommand);
3708     /* Unfortunately we need to copy the command, because it might be in
3709      * read-only storage and HandleCommand might want to modify it. */
3710     sz = g_strdup(szCommand);
3711 #ifndef WIN32
3712     if (!fTTY || !fInteractive)
3713 #endif
3714     {
3715         fInterrupt = FALSE;
3716         HandleCommand(sz, acTop);
3717         g_free(sz);
3718         ResetInterrupt();
3719         return;
3720     }
3721 
3722     /* Note that the command is always echoed to stdout; the output*()
3723      * functions are bypassed. */
3724 #if defined(HAVE_LIB_READLINE)
3725     rl_end = 0;                 /* crashes without this line */
3726     rl_redisplay();
3727     g_print("%s\n", sz);
3728     ProcessInput(sz);
3729     g_free(sz);
3730     return;
3731 #elif !defined(WIN32)
3732     g_print("\n");
3733     Prompt();
3734     g_print("%s\n", sz);
3735     fInterrupt = FALSE;
3736     HandleCommand(sz, acTop);
3737     g_free(sz);
3738     ResetInterrupt();
3739     if (nNextTurn)
3740         Prompt();
3741     else
3742         fNeedPrompt = TRUE;
3743     return;
3744 #endif
3745 }
3746 
3747 extern gint
NextTurnNotify(gpointer UNUSED (p))3748 NextTurnNotify(gpointer UNUSED(p))
3749 {
3750     NextTurn(TRUE);
3751 
3752     ResetInterrupt();
3753 
3754     if (fNeedPrompt) {
3755 #if defined(HAVE_LIB_READLINE)
3756         if (fInteractive) {
3757             char *sz = locale_from_utf8(FormatPrompt());
3758             rl_callback_handler_install(sz, ProcessInput);
3759             g_free(sz);
3760             fReadingCommand = TRUE;
3761         } else
3762 #endif
3763             Prompt();
3764 
3765         fNeedPrompt = FALSE;
3766     }
3767 
3768     return FALSE;               /* remove idle handler, if GTK */
3769 }
3770 #endif
3771 
3772 /* Read a line from stdin, and handle X and readline input if
3773  * appropriate.  This function blocks until a line is ready, and does
3774  * not call HandleEvents(), and because fBusy will be set some X
3775  * commands will not work.  Therefore, it should not be used for
3776  * reading top level commands.  The line it returns has been allocated
3777  * with malloc (as with readline()). */
3778 extern char *
GetInput(char * szPrompt)3779 GetInput(char *szPrompt)
3780 {
3781 
3782     char *sz;
3783     char *pch;
3784     char *pchConverted;
3785 #if defined(USE_GTK)
3786     g_assert(fTTY && !fX);
3787 #endif
3788 
3789 #if defined(HAVE_LIB_READLINE)
3790     if (fInteractive) {
3791         char *prompt;
3792         /* Using readline, but not X. */
3793         if (fInterrupt)
3794             return NULL;
3795 
3796         fReadingOther = TRUE;
3797 
3798         prompt = locale_from_utf8(szPrompt);
3799         while (!(sz = readline(prompt))) {
3800             outputc('\n');
3801             PromptForExit();
3802         }
3803         g_free(prompt);
3804 
3805         fReadingOther = FALSE;
3806 
3807         if (fInterrupt)
3808             return NULL;
3809 
3810         pchConverted = locale_to_utf8(sz);
3811         free(sz);
3812         return pchConverted;
3813     }
3814 #endif
3815     /* Not using readline or X. */
3816     if (fInterrupt)
3817         return NULL;
3818 
3819     g_print("%s", szPrompt);
3820     fflush(stdout);
3821 
3822     sz = malloc(256);           /* FIXME it would be nice to handle longer strings */
3823 
3824     clearerr(stdin);
3825     pch = fgets(sz, 256, stdin);
3826 
3827     if (fInterrupt) {
3828         free(sz);
3829         return NULL;
3830     }
3831 
3832     if (!pch) {
3833         if (!isatty(STDIN_FILENO))
3834             exit(EXIT_SUCCESS);
3835 
3836         outputc('\n');
3837         PromptForExit();
3838     }
3839 
3840     if ((pch = strchr(sz, '\n')))
3841         *pch = 0;
3842     if ((pch = strchr(sz, '\r')))
3843         *pch = 0;
3844 
3845     pchConverted = locale_to_utf8(sz);
3846     free(sz);
3847     return pchConverted;
3848 }
3849 
3850 /* Ask a yes/no question.  Interrupting the question is considered a "no"
3851  * answer. */
3852 extern int
GetInputYN(char * szPrompt)3853 GetInputYN(char *szPrompt)
3854 {
3855 
3856     char *pch;
3857 
3858     if (nConfirmDefault != -1) {
3859         outputf("%s %s\n", szPrompt, nConfirmDefault ? _("yes") : _("no"));
3860         return nConfirmDefault;
3861     }
3862 
3863     if (!fInteractive)
3864         return TRUE;
3865 
3866 #if defined(USE_GTK)
3867     if (fX)
3868         return GTKGetInputYN(szPrompt);
3869 #endif
3870 
3871     if (fInterrupt)
3872         return FALSE;
3873 
3874     while ((pch = GetInput(szPrompt)) != 0) {
3875         if (pch)
3876             switch (*pch) {
3877             case 'y':
3878             case 'Y':
3879                 g_free(pch);
3880                 return TRUE;
3881             case 'n':
3882             case 'N':
3883                 g_free(pch);
3884                 return FALSE;
3885             default:
3886                 g_free(pch);
3887             }
3888 
3889         outputl(_("Please answer `y' or `n'."));
3890     }
3891     return FALSE;
3892 }
3893 
3894 /* Like strncpy, except it does the right thing */
3895 extern char *
strcpyn(char * szDest,const char * szSrc,int cch)3896 strcpyn(char *szDest, const char *szSrc, int cch)
3897 {
3898 
3899     char *pchDest = szDest;
3900     const char *pchSrc = szSrc;
3901 
3902     if (cch-- < 1)
3903         return szDest;
3904 
3905     while (cch--)
3906         if (!(*pchDest++ = *pchSrc++))
3907             return szDest;
3908 
3909     *pchDest = 0;
3910 
3911     return szDest;
3912 }
3913 
3914 extern void
CommandSetOutputOutput(char * sz)3915 CommandSetOutputOutput(char *sz)
3916 {
3917     SetToggle("output", &foutput_on, sz, _("output will be shown"), _("output will not be shown"));
3918     return;
3919 }
3920 
3921 static GTimeVal tvProgress;
3922 
3923 static int
ProgressThrottle(void)3924 ProgressThrottle(void)
3925 {
3926     GTimeVal tv, tvDiff;
3927     g_get_current_time(&tv);
3928 
3929     tvDiff.tv_sec = tv.tv_sec - tvProgress.tv_sec;
3930     if ((tvDiff.tv_usec = tv.tv_usec + 1000000 - tvProgress.tv_usec) >= 1000000)
3931         tvDiff.tv_usec -= 1000000;
3932     else
3933         tvDiff.tv_sec--;
3934 
3935     if (tvDiff.tv_sec || tvDiff.tv_usec >= 100000) {
3936         /* sufficient time elapsed; record current time */
3937         tvProgress.tv_sec = tv.tv_sec;
3938         tvProgress.tv_usec = tv.tv_usec;
3939         return 0;
3940     }
3941 
3942     /* insufficient time elapsed */
3943     return -1;
3944 }
3945 
3946 extern void
ProgressStart(const char * sz)3947 ProgressStart(const char *sz)
3948 {
3949     if (!fShowProgress)
3950         return;
3951 
3952     fInProgress = TRUE;
3953 
3954 #if defined(USE_GTK)
3955     if (fX) {
3956         GTKProgressStart(sz);
3957         return;
3958     }
3959 #endif
3960 
3961     if (sz) {
3962         fputs(sz, stdout);
3963         fflush(stdout);
3964     }
3965 }
3966 
3967 
3968 extern void
ProgressStartValue(char * sz,int iMax)3969 ProgressStartValue(char *sz, int iMax)
3970 {
3971 
3972     if (!fShowProgress)
3973         return;
3974 
3975     iProgressMax = iMax;
3976     iProgressValue = 0;
3977     pcProgress = sz;
3978 
3979     fInProgress = TRUE;
3980 
3981 #if defined(USE_GTK)
3982     if (fX) {
3983         GTKProgressStartValue(sz, iMax);
3984         return;
3985     }
3986 #endif
3987 
3988     if (sz) {
3989         fputs(sz, stdout);
3990         fflush(stdout);
3991     }
3992 
3993 }
3994 
3995 
3996 extern void
ProgressValue(int iValue)3997 ProgressValue(int iValue)
3998 {
3999 
4000     if (!fShowProgress || iProgressValue == iValue)
4001         return;
4002 
4003     iProgressValue = iValue;
4004 
4005     if (ProgressThrottle())
4006         return;
4007 #if defined(USE_GTK)
4008     if (fX) {
4009         GTKProgressValue(iValue, iProgressMax);
4010         return;
4011     }
4012 #endif
4013 
4014     outputf("\r%s %d/%d\r", pcProgress, iProgressValue, iProgressMax);
4015     fflush(stdout);
4016 
4017 }
4018 
4019 
4020 extern void
ProgressValueAdd(int iValue)4021 ProgressValueAdd(int iValue)
4022 {
4023 
4024     ProgressValue(iProgressValue + iValue);
4025 
4026 }
4027 
4028 
4029 static gboolean
Progress(gpointer UNUSED (unused))4030 Progress(gpointer UNUSED(unused))
4031 {
4032     static int i = 0;
4033     static char ach[5] = "/-\\|";
4034 
4035     if (!fShowProgress)
4036         return FALSE;
4037 
4038     if (ProgressThrottle())
4039         return TRUE;
4040 #if defined(USE_GTK)
4041     if (fX) {
4042         GTKProgress();
4043         return TRUE;
4044     }
4045 #endif
4046 
4047     putchar(ach[i++]);
4048     i &= 0x03;
4049     putchar('\b');
4050     fflush(stdout);
4051     return TRUE;
4052 }
4053 
4054 #if !defined(USE_MULTITHREAD)
4055 extern void
CallbackProgress(void)4056 CallbackProgress(void)
4057 {
4058 #if defined(USE_GTK)
4059     if (fX) {
4060         GTKDisallowStdin();
4061         if (fInProgress)
4062             GTKSuspendInput();
4063 
4064         while (gtk_events_pending())
4065             gtk_main_iteration();
4066 
4067         if (fInProgress)
4068             GTKResumeInput();
4069         GTKAllowStdin();
4070     }
4071 #endif
4072 
4073     if (fInProgress && !iProgressMax)
4074         Progress(NULL);
4075 }
4076 #endif
4077 
4078 extern void
ProgressEnd(void)4079 ProgressEnd(void)
4080 {
4081 
4082     int i;
4083 
4084     if (!fShowProgress)
4085         return;
4086 
4087     fInProgress = FALSE;
4088     iProgressMax = 0;
4089     iProgressValue = 0;
4090     pcProgress = NULL;
4091 
4092 #if defined(USE_GTK)
4093     if (fX) {
4094         GTKProgressEnd();
4095         return;
4096     }
4097 #endif
4098 
4099     putchar('\r');
4100     for (i = 0; i < 79; i++)
4101         putchar(' ');
4102     putchar('\r');
4103     fflush(stdout);
4104 
4105 }
4106 
4107 static void
move_rc_files(void)4108 move_rc_files(void)
4109 {
4110     /* Move files to the new location. Remove this part when all users have had
4111      * their files moved.*/
4112     char *olddir, *oldfile, *newfile;
4113 #ifdef WIN32
4114     olddir = g_strdup(getDataDir());
4115 #else
4116     olddir = g_build_filename(szHomeDirectory, "..", NULL);
4117 #endif
4118 
4119     newfile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
4120     oldfile = g_build_filename(olddir, ".gnubgautorc", NULL);
4121     if (g_file_test(oldfile, G_FILE_TEST_IS_REGULAR) && !g_file_test(newfile, G_FILE_TEST_EXISTS))
4122         g_rename(oldfile, newfile);
4123     g_free(oldfile);
4124     g_free(newfile);
4125 
4126     newfile = g_build_filename(szHomeDirectory, "gnubgrc", NULL);
4127     oldfile = g_build_filename(olddir, ".gnubgrc", NULL);
4128     if (g_file_test(oldfile, G_FILE_TEST_IS_REGULAR) && !g_file_test(newfile, G_FILE_TEST_EXISTS))
4129         g_rename(oldfile, newfile);
4130     g_free(oldfile);
4131     g_free(newfile);
4132 
4133     g_free(olddir);
4134 
4135 }
4136 
4137 /*
4138  * Create $HOME/.gnubg if not existing
4139  *
4140  */
4141 
4142 static int
CreateGnubgDirectory(void)4143 CreateGnubgDirectory(void)
4144 {
4145     char *newfile;
4146 
4147     if (!g_file_test(szHomeDirectory, G_FILE_TEST_IS_DIR)) {
4148         if (g_mkdir(szHomeDirectory, 0700) < 0) {
4149             outputerr(szHomeDirectory);
4150             return -1;
4151         }
4152     }
4153     newfile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
4154     if (!g_file_test(newfile, G_FILE_TEST_IS_REGULAR))
4155         move_rc_files();
4156     g_free(newfile);
4157     return 0;
4158 }
4159 
4160 
4161 extern void
HandleInterrupt(int UNUSED (idSignal))4162 HandleInterrupt(int UNUSED(idSignal))
4163 {
4164     /* NB: It is safe to write to fInterrupt even if it cannot be read
4165      * atomically, because it is only used to hold a binary value. */
4166     fInterrupt = TRUE;
4167 }
4168 
4169 static void
BearoffProgress(unsigned int i)4170 BearoffProgress(unsigned int i)
4171 {
4172 #if defined(USE_GTK)
4173     if (fX) {
4174         GTKBearoffProgress(i);
4175         return;
4176     }
4177 #endif
4178     putchar("\\|/-"[(i / 1000) % 4]);
4179     putchar('\r');
4180     fflush(stdout);
4181 }
4182 
4183 static void
VersionMessage(void)4184 VersionMessage(void)
4185 {
4186     g_print("%s\n%s\n", _(VERSION_STRING), _(aszCOPYRIGHT));
4187     g_print("%s", _(intro_string));
4188 }
4189 
4190 #if defined(HAVE_LIB_READLINE)
4191 static char *
get_readline(void)4192 get_readline(void)
4193 {
4194     char *pchExpanded;
4195     char *szInput;
4196     char *sz;
4197     char *prompt = locale_from_utf8(FormatPrompt());
4198     while (!(szInput = readline(prompt))) {
4199         outputc('\n');
4200         PromptForExit();
4201     }
4202     g_free(prompt);
4203     sz = locale_to_utf8(szInput);
4204     free(szInput);
4205     fInterrupt = FALSE;
4206     history_expand(sz, &pchExpanded);
4207     g_free(sz);
4208     if (*pchExpanded)
4209         add_history(pchExpanded);
4210     return pchExpanded;
4211 }
4212 #endif
4213 
4214 static char *
get_stdin_line(void)4215 get_stdin_line(void)
4216 {
4217     char sz[2048], *pch;
4218 
4219     sz[0] = 0;
4220     Prompt();
4221 
4222     clearerr(stdin);
4223 
4224     /* FIXME shouldn't restart sys calls on signals during this
4225      * fgets */
4226     if (fgets(sz, sizeof(sz), stdin) == NULL) {
4227         if (ferror(stdin)) {
4228             outputerr("stdin");
4229             exit(EXIT_FAILURE);
4230         } else {
4231             Shutdown();
4232             exit(EXIT_SUCCESS);
4233         }
4234     }
4235 
4236     if ((pch = strchr(sz, '\n')))
4237         *pch = 0;
4238 
4239 
4240     if (feof(stdin)) {
4241         if (!isatty(STDIN_FILENO)) {
4242             Shutdown();
4243             exit(EXIT_SUCCESS);
4244         }
4245 
4246         outputc('\n');
4247 
4248         if (!sz[0])
4249             PromptForExit();
4250         return NULL;
4251     }
4252 
4253     fInterrupt = FALSE;
4254     return strdup(sz);
4255 }
4256 
4257 
4258 
4259 static void
run_cl(void)4260 run_cl(void)
4261 {
4262     char *line;
4263     for (;;) {
4264 #if defined(HAVE_LIB_READLINE)
4265         if (fInteractive) {
4266             line = get_readline();
4267             HandleCommand(line, acTop);
4268             free(line);
4269         } else
4270 #endif
4271         {
4272             line = get_stdin_line();
4273             HandleCommand(line, acTop);
4274             free(line);
4275         }
4276         while (fNextTurn)
4277             NextTurn(TRUE);
4278         ResetInterrupt();
4279     }
4280 }
4281 
4282 static void
init_language(char ** lang)4283 init_language(char **lang)
4284 {
4285     char *szFile, szTemp[4096];
4286     char *pch;
4287     FILE *pf;
4288 
4289     outputoff();
4290     szFile = g_build_filename(szHomeDirectory, "gnubgautorc", NULL);
4291 
4292     if (!*lang && (pf = gnubg_g_fopen(szFile, "r"))) {
4293 
4294         while (fgets(szTemp, sizeof(szTemp), pf) != NULL) {
4295             if ((pch = strchr(szTemp, '\n')))
4296                 *pch = 0;
4297             if ((pch = strchr(szTemp, '\r')))
4298                 *pch = 0;
4299 
4300             if (!strncmp("set lang", szTemp, 8)) {
4301                 *lang = g_strdup(szTemp + 9);
4302                 break;
4303             }
4304         }
4305         if (ferror(pf))
4306             outputerr(szFile);
4307 
4308         fclose(pf);
4309     }
4310     g_free(szFile);
4311     CommandSetLang(*lang);
4312     g_free(*lang);
4313     outputon();
4314 }
4315 
4316 static void
setup_readline(void)4317 setup_readline(void)
4318 {
4319 #if defined(HAVE_LIB_READLINE)
4320     char *pch;
4321     int i;
4322     gnubg_histfile = g_build_filename(szHomeDirectory, "history", NULL);
4323     rl_readline_name = "gnubg";
4324     rl_basic_word_break_characters = rl_filename_quote_characters = szCommandSeparators;
4325     rl_completer_quote_characters = "\"";
4326     /* assume readline 4.2 or later */
4327     rl_completion_entry_function = NullGenerator;
4328     rl_attempted_completion_function = CompleteKeyword;
4329     /* setup history */
4330     read_history(gnubg_histfile);
4331     using_history();
4332     if ((pch = getenv("HISTSIZE")) && ((i = atoi(pch)) > 0))
4333         stifle_history(i);
4334 #endif
4335 }
4336 
4337 #if !defined(USE_GTK)
4338 static void
PushSplash(char * UNUSED (unused),char * UNUSED (heading),char * UNUSED (message))4339 PushSplash(char *UNUSED(unused), char *UNUSED(heading), char *UNUSED(message))
4340 {
4341 }
4342 #endif
4343 
4344 static void
init_nets(int fNoBearoff)4345 init_nets(int fNoBearoff)
4346 {
4347     char *gnubg_weights = BuildFilename("gnubg.weights");
4348     char *gnubg_weights_binary = BuildFilename("gnubg.wd");
4349     EvalInitialise(gnubg_weights, gnubg_weights_binary, fNoBearoff, fShowProgress ? BearoffProgress : NULL);
4350     g_free(gnubg_weights);
4351     g_free(gnubg_weights_binary);
4352 }
4353 
4354 extern int
GetManualDice(unsigned int anDice[2])4355 GetManualDice(unsigned int anDice[2])
4356 {
4357 
4358     char *pz;
4359     char *sz = NULL;
4360     int i;
4361 
4362 #if defined(USE_GTK)
4363     if (fX) {
4364         if (GTKGetManualDice(anDice)) {
4365             fInterrupt = 1;
4366             return -1;
4367         } else
4368             return 0;
4369     }
4370 #endif
4371 
4372     for (;;) {
4373       TryAgain:
4374         if (fInterrupt) {
4375             anDice[0] = anDice[1] = 0;
4376             return -1;
4377         }
4378 
4379         sz = GetInput(_("Enter dice: "));
4380 
4381         if (fInterrupt) {
4382             g_free(sz);
4383             anDice[0] = anDice[1] = 0;
4384             return -1;
4385         }
4386 
4387         /* parse input and read a couple of dice */
4388         /* any string with two numbers is allowed */
4389 
4390         pz = sz;
4391 
4392         for (i = 0; i < 2; i++) {
4393             while (*pz && ((*pz < '1') || (*pz > '6')))
4394                 pz++;
4395 
4396             if (!*pz) {
4397                 outputl(_("You must enter two numbers between 1 and 6."));
4398                 goto TryAgain;
4399             }
4400 
4401             anDice[i] = (int) (*pz - '0');
4402             pz++;
4403         }
4404 
4405         g_free(sz);
4406         return 0;
4407     }
4408 }
4409 
4410 static void
init_rng(void)4411 init_rng(void)
4412 {
4413     if (!(rngctxCurrent = InitRNG(NULL, NULL, TRUE, rngCurrent))) {
4414         printf("%s\n", _("Failure setting up RNG"));
4415         exit(EXIT_FAILURE);
4416     }
4417     if (!(rngctxRollout = InitRNG(&rcRollout.nSeed, NULL, TRUE, rcRollout.rngRollout))) {
4418         printf("%s\n", _("Failure setting up RNG for rollout."));
4419         exit(EXIT_FAILURE);
4420     }
4421 
4422     /* we don't want rollouts to use the same seed as normal dice (which
4423      * could happen if InitRNG had to use the current time as a seed) -- mix
4424      * it up a little bit */
4425     rcRollout.nSeed ^= 0x792A584B;
4426 }
4427 
4428 #if defined(WIN32) && defined(HAVE_SOCKETS)
4429 static void
init_winsock()4430 init_winsock()
4431 {
4432     short wVersionRequested;
4433     WSADATA wsaData;
4434     wVersionRequested = MAKEWORD(1, 1);
4435     if (WSAStartup(wVersionRequested, &wsaData) != 0) {
4436         outputerr("Windows sockets initialisation");
4437     }
4438 }
4439 #endif
4440 
4441 static char *
matchfile_from_argv(char * sz)4442 matchfile_from_argv(char *sz)
4443 {
4444     char *pchMatch;
4445 #ifdef WIN32
4446     gsize br, bw;
4447     gchar *utf8fn;
4448 
4449     if (g_path_is_absolute(sz))
4450         pchMatch = g_strdup_printf("\"%s\"", sz);
4451     else {
4452         char *tmp = g_build_filename(g_get_current_dir(), sz, NULL);
4453         pchMatch = g_strdup_printf("\"%s\"", tmp);
4454         g_free(tmp);
4455     }
4456     utf8fn = g_locale_to_utf8(pchMatch, -1, &br, &bw, NULL);
4457     g_free(pchMatch);
4458     pchMatch = utf8fn;
4459 #else
4460     pchMatch = g_strdup_printf("\"%s\"", sz);
4461 #endif
4462     return pchMatch;
4463 }
4464 
4465 static void
init_defaults(void)4466 init_defaults(void)
4467 {
4468     /* init some html export options */
4469 
4470     exsExport.szHTMLPictureURL = g_strdup("html-images/");
4471     exsExport.szHTMLExtension = g_strdup("png");
4472 
4473     SetMatchDate(&mi);
4474 
4475     strcpy(ap[1].szName, g_get_user_name());
4476     strcpy(default_names[1], g_get_user_name());
4477 
4478     ListCreate(&lMatch);
4479     IniStatcontext(&scMatch);
4480 
4481     szHomeDirectory = g_build_filename(g_get_home_dir(), ".gnubg", NULL);
4482 
4483 }
4484 
4485 static void
init_autosave(void)4486 init_autosave(void)
4487 {
4488     char *backupdir;
4489     backupdir = g_build_filename(szHomeDirectory, "backup", NULL);
4490     g_mkdir(backupdir, 0700);
4491     g_free(backupdir);
4492 }
4493 
4494 static void
null_debug(const gchar * UNUSED (dom),GLogLevelFlags UNUSED (logflags),const gchar * UNUSED (message),gpointer UNUSED (unused))4495 null_debug(const gchar * UNUSED(dom), GLogLevelFlags UNUSED(logflags), const gchar * UNUSED(message),
4496            gpointer UNUSED(unused))
4497 {
4498 }
4499 
4500 int
main(int argc,char * argv[])4501 main(int argc, char *argv[])
4502 {
4503 #if defined(USE_GTK)
4504     GtkWidget *pwSplash = NULL;
4505 #else
4506     char *pwSplash = NULL;
4507 #endif
4508     char *pchMatch = NULL;
4509     char *met = NULL;
4510 
4511     static char *pchCommands = NULL, *pchPythonScript = NULL, *lang = NULL;
4512     static int fNoBearoff = FALSE, fNoX = FALSE, fSplash = FALSE, fNoTTY = FALSE, show_version = FALSE, debug = FALSE;
4513     GOptionEntry ao[] = {
4514         {"no-bearoff", 'b', 0, G_OPTION_ARG_NONE, &fNoBearoff,
4515          N_("Do not use bearoff database"), NULL},
4516         {"commands", 'c', 0, G_OPTION_ARG_FILENAME, &pchCommands,
4517          N_("Evaluate commands in FILE and exit"), "FILE"},
4518         {"lang", 'l', 0, G_OPTION_ARG_STRING, &lang,
4519          N_("Set language to LANG"), "LANG"},
4520         {"python", 'p', 0, G_OPTION_ARG_FILENAME, &pchPythonScript,
4521          N_("Evaluate Python code in FILE and exit"), "FILE"},
4522         {"quiet", 'q', 0, G_OPTION_ARG_NONE, &fQuiet,
4523          N_("Disable sound effects"), NULL},
4524         {"no-rc", 'r', 0, G_OPTION_ARG_NONE, &fNoRC,
4525          N_("Do not read .gnubgrc and .gnubgautorc commands"),
4526          NULL},
4527         {"splash", 'S', 0, G_OPTION_ARG_NONE, &fSplash,
4528          N_("Show gtk splash screen"), NULL},
4529         {"tty", 't', 0, G_OPTION_ARG_NONE, &fNoX,
4530          N_("Start the command-line instead of using the graphical interface"), NULL},
4531         {"version", 'v', 0, G_OPTION_ARG_NONE, &show_version,
4532          N_("Show version information and exit"), NULL},
4533         {"window-system-only", 'w', 0, G_OPTION_ARG_NONE, &fNoTTY,
4534          N_("Ignore tty input when using the graphical interface"), NULL},
4535         {"debug", 'd', 0, G_OPTION_ARG_NONE, &debug,
4536          N_("Turn on debug"), NULL},
4537         {"datadir", 'D', 0, G_OPTION_ARG_STRING, &datadir,
4538          N_("Specify location of general data"), NULL},
4539         {"pkgdatadir", 'P', 0, G_OPTION_ARG_STRING, &pkg_datadir,
4540          N_("Specify location of program specific data"), NULL},
4541         {"docdir", 'O', 0, G_OPTION_ARG_STRING, &docdir,
4542          N_("Specify location of program documentation"), NULL},
4543         {"prefsdir", 's', 0, G_OPTION_ARG_STRING, &prefsdir,
4544          N_("Specify location of user's preferences directory"), NULL},
4545         {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}
4546     };
4547     GError *error = NULL;
4548     GOptionContext *context;
4549 
4550 #if ! GLIB_CHECK_VERSION(2,36,0)
4551     g_type_init();
4552 #endif
4553 
4554 #if defined(LIBCURL_PROTOCOL_HTTPS)
4555     curl_global_init(CURL_GLOBAL_ALL);
4556 #endif
4557 
4558     default_import_folder = g_strdup(".");
4559     default_export_folder = g_strdup(".");
4560     default_sgf_folder = g_strdup(".");
4561 
4562     output_initialize();
4563 
4564     /* set language */
4565     init_defaults();
4566 #if defined(USE_GTK)
4567     gtk_disable_setlocale();
4568 #endif
4569     init_language(&lang);
4570     bindtextdomain(PACKAGE, LOCALEDIR);
4571     textdomain(PACKAGE);
4572     bind_textdomain_codeset(PACKAGE, GNUBG_CHARSET);
4573 
4574     /* parse command-line options */
4575     context = g_option_context_new("[file.sgf]");
4576     g_option_context_add_main_entries(context, ao, PACKAGE);
4577 #if defined(USE_GTK)
4578     g_option_context_add_group(context, gtk_get_option_group(FALSE));
4579 #endif
4580 
4581     g_option_context_parse(context, &argc, &argv, &error);
4582     g_option_context_free(context);
4583     if (error) {
4584         outputerrf("%s\n", error->message);
4585         exit(EXIT_FAILURE);
4586     }
4587     if (argc > 1 && *argv[1])
4588         pchMatch = matchfile_from_argv(argv[1]);
4589 
4590     if (!debug)
4591         g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, &null_debug, NULL);
4592 
4593     if (prefsdir) {
4594         szHomeDirectory = prefsdir;
4595     }
4596 #ifdef WIN32
4597     /* data directory: initialise to the path where gnubg is installed */
4598     {
4599         const char *szDataDirectory = getDataDir();
4600         _chdir(szDataDirectory);
4601     }
4602     /* Create a mutex so install can check if it's running */
4603     CreateMutex(NULL, FALSE, "GNU Backgammon Mutex");
4604 
4605 #endif
4606 
4607     /* print version and exit if -v option given */
4608     VersionMessage();
4609     if (show_version)
4610         exit(EXIT_SUCCESS);
4611 
4612     if (CreateGnubgDirectory())
4613         exit(EXIT_FAILURE);
4614 
4615     init_autosave();
4616 
4617     RenderInitialise();
4618 
4619 #ifdef WIN32
4620     fNoTTY = TRUE;
4621 #endif
4622 #if defined(USE_GTK)
4623     /* -t option not given */
4624     if (!fNoX)
4625         InitGTK(&argc, &argv);
4626     if (fX) {
4627         fTTY = !fNoTTY && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO);
4628         fInteractive = fShowProgress = TRUE;
4629         if (fSplash)
4630             pwSplash = CreateSplash();
4631     } else
4632 #endif
4633     {
4634         fInteractive = isatty(STDIN_FILENO);
4635         fShowProgress = isatty(STDOUT_FILENO);
4636     }
4637 
4638     if (fInteractive) {
4639         PortableSignal(SIGINT, HandleInterrupt, &shInterruptOld, FALSE);
4640         setup_readline();
4641     }
4642 
4643     PushSplash(pwSplash, _("Initialising"), _("Random number generator"));
4644     init_rng();
4645 
4646     PushSplash(pwSplash, _("Initialising"), _("match equity table"));
4647     met = BuildFilename2("met", "Kazaross-XG2.xml");
4648     InitMatchEquity(met);
4649     g_free(met);
4650 
4651     PushSplash(pwSplash, _("Initialising"), _("neural nets"));
4652     init_nets(fNoBearoff);
4653 
4654     PushSplash(pwSplash, _("Initialising"), _("initialising thread data"));
4655     glib_ext_init();
4656     MT_InitThreads();
4657 
4658 #if defined(WIN32) && defined(HAVE_SOCKETS)
4659     PushSplash(pwSplash, _("Initialising"), _("Windows sockets"));
4660     init_winsock();
4661 #endif
4662 
4663 #if defined(USE_PYTHON)
4664     PushSplash(pwSplash, _("Initialising"), "Python");
4665     PythonInitialise(argv[0]);
4666 #endif
4667 
4668     SetExitSoundOff();
4669 
4670     /* -r option given */
4671     if (!fNoRC) {
4672         PushSplash(pwSplash, _("Loading"), _("User Settings"));
4673         LoadRCFiles();
4674     }
4675 
4676     strcpy(ap[0].szName, default_names[0]);
4677     strcpy(ap[1].szName, default_names[1]);
4678 
4679     fflush(stdout);
4680     fflush(stderr);
4681 
4682 #if defined(USE_MULTITHREAD)
4683     /* Make sure threads started */
4684     MT_StartThreads();
4685 #endif
4686 
4687     /* start-up sound */
4688     playSound(SOUND_START);
4689 
4690 #if defined(USE_GTK)
4691     if (fX) {
4692         if (!fTTY) {
4693             g_set_print_handler(&GTKOutput);
4694             g_set_printerr_handler(&GTKOutputErr);
4695         }
4696         RunGTK(pwSplash, pchCommands, pchPythonScript, pchMatch);
4697         Shutdown();
4698         exit(EXIT_SUCCESS);
4699     }
4700 #endif
4701 
4702     if (pchMatch)
4703         CommandImportAuto(pchMatch);
4704 
4705     /* -c option given */
4706     if (pchCommands) {
4707         fInteractive = FALSE;
4708         CommandLoadCommands(pchCommands);
4709         Shutdown();
4710         exit(EXIT_SUCCESS);
4711     }
4712 
4713     /* -p option given */
4714     if (pchPythonScript) {
4715 #if defined(USE_PYTHON)
4716         fInteractive = FALSE;
4717         LoadPythonFile(pchPythonScript, FALSE);
4718         Shutdown();
4719         exit(EXIT_SUCCESS);
4720 #else
4721         outputerrf(_("GNU Backgammon build without Python."));
4722         exit(EXIT_FAILURE);
4723 #endif
4724     }
4725     run_cl();
4726     return (EXIT_FAILURE);
4727 }
4728 
4729 
4730 /*
4731  * Command: convert normalised money equity to match winning chance.
4732  *
4733  * The result is written to stdout.
4734  * FIXME: implement GTK version.
4735  * FIXME: allow more parameters (match score, match length)
4736  *
4737  * Input:
4738  *   sz: string with equity
4739  *
4740  * Output:
4741  *   none.
4742  *
4743  */
4744 
4745 extern void
CommandEq2MWC(char * sz)4746 CommandEq2MWC(char *sz)
4747 {
4748 
4749     float rEq;
4750     cubeinfo ci;
4751 
4752     if (!ms.nMatchTo) {
4753         outputl(_("Command only valid in match play"));
4754         return;
4755     }
4756 
4757 
4758     rEq = ParseReal(&sz);
4759 
4760     if (rEq == ERR_VAL)
4761         rEq = 0.0;
4762 
4763     GetMatchStateCubeInfo(&ci, &ms);
4764 
4765     outputf("%s = %+6.3f: %6.2f%%\n", _("MWC for equity"), -1.0, 100.0 * eq2mwc(-1.0, &ci));
4766     outputf("%s = %+6.3f: %6.2f%%\n", _("MWC for equity"), +1.0, 100.0 * eq2mwc(+1.0, &ci));
4767     outputf("%s:\n", _("By linear interpolation"));
4768     outputf("%s = %+6.3f: %6.2f%%\n", _("MWC for equity"), rEq, 100.0 * eq2mwc(rEq, &ci));
4769 
4770 }
4771 
4772 
4773 /*
4774  * Command: convert match winning chance to normalised money equity.
4775  *
4776  * The result is written to stdout.
4777  * FIXME: implement GTK version.
4778  * FIXME: allow more parameters (match score, match length)
4779  *
4780  * Input:
4781  *   sz: string with match winning chance
4782  *
4783  * Output:
4784  *   none.
4785  *
4786  */
4787 
4788 extern void
CommandMWC2Eq(char * sz)4789 CommandMWC2Eq(char *sz)
4790 {
4791 
4792     float rMwc;
4793     cubeinfo ci;
4794 
4795     if (!ms.nMatchTo) {
4796         outputl(_("Command only valid in match play"));
4797         return;
4798     }
4799 
4800     GetMatchStateCubeInfo(&ci, &ms);
4801 
4802     rMwc = ParseReal(&sz);
4803 
4804     if (rMwc == ERR_VAL)
4805         rMwc = eq2mwc(0.0, &ci);
4806 
4807     if (rMwc > 1.0f)
4808         rMwc /= 100.0f;
4809 
4810     outputf("%s = %6.2f%%: %+6.3f\n", _("Equity for MWC"), 100.0 * eq2mwc(-1.0, &ci), -1.0);
4811     outputf("%s = %6.2f%%: %+6.3f\n", _("Equity for MWC"), 100.0 * eq2mwc(+1.0, &ci), +1.0);
4812     outputf("%s:\n", _("By linear interpolation"));
4813     outputf("%s = %6.2f%%: %+6.3f\n", _("Equity for MWC"), 100.0 * rMwc, mwc2eq(rMwc, &ci));
4814 
4815 
4816 }
4817 
4818 static void
swapGame(listOLD * plGame)4819 swapGame(listOLD * plGame)
4820 {
4821 
4822     listOLD *pl;
4823     moverecord *pmr;
4824     int n;
4825     TanBoard anBoard;
4826 
4827     for (pl = plGame->plNext; pl != plGame; pl = pl->plNext) {
4828 
4829         pmr = pl->p;
4830 
4831         switch (pmr->mt) {
4832         case MOVE_GAMEINFO:
4833 
4834             /* swap score */
4835 
4836             n = pmr->g.anScore[0];
4837             pmr->g.anScore[0] = pmr->g.anScore[1];
4838             pmr->g.anScore[1] = n;
4839 
4840             /* swap winner */
4841 
4842             if (pmr->g.fWinner > -1)
4843                 pmr->g.fWinner = !pmr->g.fWinner;
4844 
4845             /* swap statcontext */
4846 
4847             /* recalculate statcontext later ... */
4848 
4849             break;
4850 
4851         case MOVE_DOUBLE:
4852         case MOVE_TAKE:
4853         case MOVE_DROP:
4854         case MOVE_NORMAL:
4855         case MOVE_RESIGN:
4856         case MOVE_SETDICE:
4857         case MOVE_SETCUBEVAL:
4858 
4859             pmr->fPlayer = !pmr->fPlayer;
4860             break;
4861 
4862         case MOVE_SETBOARD:
4863 
4864             PositionFromKey(anBoard, &pmr->sb.key);
4865             SwapSides(anBoard);
4866             PositionKey((ConstTanBoard) anBoard, &pmr->sb.key);
4867             pmr->fPlayer = !pmr->fPlayer;
4868             break;
4869 
4870         case MOVE_SETCUBEPOS:
4871 
4872             if (pmr->scp.fCubeOwner > -1)
4873                 pmr->scp.fCubeOwner = !pmr->scp.fCubeOwner;
4874             pmr->fPlayer = !pmr->fPlayer;
4875             break;
4876 
4877         }
4878 
4879     }
4880 
4881 }
4882 
4883 
4884 
4885 extern void
CommandSwapPlayers(char * UNUSED (sz))4886 CommandSwapPlayers(char *UNUSED(sz))
4887 {
4888     listOLD *pl;
4889     char *pc;
4890     int n;
4891 
4892     /* swap individual move records */
4893 
4894     for (pl = lMatch.plNext; pl != &lMatch; pl = pl->plNext) {
4895 
4896         swapGame(pl->p);
4897 
4898     }
4899 
4900     /* swap player names */
4901 
4902     pc = g_strdup(ap[0].szName);
4903     strcpy(ap[0].szName, ap[1].szName);
4904     strcpy(ap[1].szName, pc);
4905     g_free(pc);
4906 
4907     /* swap player ratings */
4908 
4909     pc = mi.pchRating[0];
4910     mi.pchRating[0] = mi.pchRating[1];
4911     mi.pchRating[1] = pc;
4912 
4913     /* swap current matchstate */
4914 
4915     if (ms.fTurn > -1)
4916         ms.fTurn = !ms.fTurn;
4917     if (ms.fMove > -1)
4918         ms.fMove = !ms.fMove;
4919     if (ms.fCubeOwner > -1)
4920         ms.fCubeOwner = !ms.fCubeOwner;
4921     n = ms.anScore[0];
4922     ms.anScore[1] = ms.anScore[0];
4923     ms.anScore[0] = n;
4924     SwapSides(ms.anBoard);
4925 
4926 #if defined(USE_GTK)
4927     if (fX) {
4928         fJustSwappedPlayers = TRUE;
4929         GTKSet(ap);
4930         /* GTKSet(ap) already does this. This just adds flicker.
4931          * GTKRegenerateGames();
4932          */
4933     }
4934 #endif
4935 
4936     ChangeGame(NULL);
4937 
4938     ShowBoard();
4939 }
4940 
4941 
4942 extern int
confirmOverwrite(const char * sz,const int f)4943 confirmOverwrite(const char *sz, const int f)
4944 {
4945 
4946     char *szPrompt;
4947     int i;
4948 
4949     /* check for existing file */
4950 
4951     if (f && !access(sz, F_OK)) {
4952 
4953         szPrompt = (char *) malloc(50 + strlen(sz));
4954         sprintf(szPrompt, _("File \"%s\" exists. Overwrite? "), sz);
4955         i = GetInputYN(szPrompt);
4956         free(szPrompt);
4957         return i;
4958 
4959     } else
4960         return TRUE;
4961 
4962 
4963 }
4964 
4965 extern void
setDefaultFileName(char * path)4966 setDefaultFileName(char *path)
4967 {
4968     g_free(szCurrentFolder);
4969     g_free(szCurrentFileName);
4970     DisectPath(path, NULL, &szCurrentFileName, &szCurrentFolder);
4971 #if defined(USE_GTK)
4972     if (fX) {
4973         gchar *title = g_strdup_printf("%s (%s)", _("GNU Backgammon"), szCurrentFileName);
4974         gtk_window_set_title(GTK_WINDOW(pwMain), title);
4975         g_free(title);
4976     }
4977 #endif
4978 }
4979 
4980 extern void
DisectPath(const char * path,const char * extension,char ** name,char ** folder)4981 DisectPath(const char *path, const char *extension, char **name, char **folder)
4982 {
4983     char *fnn, *pc;
4984     if (!path) {
4985         *folder = NULL;
4986         *name = NULL;
4987         return;
4988     }
4989     *folder = g_path_get_dirname(path);
4990     fnn = g_path_get_basename(path);
4991     pc = strrchr(fnn, '.');
4992     if (pc)
4993         *pc = '\0';
4994     *name = g_strconcat(fnn, extension, NULL);
4995     g_free(fnn);
4996 }
4997 
4998 
4999 /* ask for confirmation if this is a sub-optimal play
5000  * returns TRUE if player wants to re-think the move
5001  */
5002 
5003 static int
GetAdviceAnswer(char * sz)5004 GetAdviceAnswer(char *sz)
5005 {
5006 
5007     char *pch;
5008 #if defined(USE_GTK)
5009     if (fX)
5010         return GtkTutor(sz);
5011 #endif
5012 
5013     if (fInterrupt)
5014         return FALSE;
5015 
5016     while ((pch = GetInput(sz)) != 0) {
5017         if (pch)
5018             switch (*pch) {
5019             case 'y':
5020             case 'Y':
5021                 free(pch);
5022                 return TRUE;
5023             case 'n':
5024             case 'N':
5025                 free(pch);
5026                 return FALSE;
5027             default:
5028                 free(pch);
5029             }
5030 
5031         outputl(_("Please answer `y' or `n'."));
5032     }
5033     return FALSE;
5034 }
5035 
5036 extern int
GiveAdvice(skilltype Skill)5037 GiveAdvice(skilltype Skill)
5038 {
5039 
5040     char *sz;
5041 
5042     /* should never happen */
5043     if (!fTutor)
5044         return FALSE;
5045 
5046     switch (Skill) {
5047 
5048     case SKILL_VERYBAD:
5049         sz = _("You may be about to make a very bad play");
5050         break;
5051 
5052     case SKILL_BAD:
5053         sz = _("You may be about to make a bad play");
5054         break;
5055 
5056     case SKILL_DOUBTFUL:
5057         sz = _("You may be about to make a doubtful play");
5058         break;
5059 
5060     default:
5061         return (TRUE);
5062     }
5063 
5064     if (Skill > TutorSkill)
5065         return (TRUE);
5066 
5067     {
5068         int ret;
5069         char *buf = g_strdup_printf("%s. %s", sz, _("Are you sure?"));
5070         ret = GetAdviceAnswer(buf);
5071         g_free(buf);
5072         return ret;
5073     }
5074 }
5075 
5076 extern void
TextToClipboard(const char * sz)5077 TextToClipboard(const char *sz)
5078 {
5079 #if defined(USE_GTK)
5080     if (fX)
5081         GTKTextToClipboard(sz);
5082 #else
5083     /* no clipboard: just write string */
5084     outputl(sz);
5085 #endif
5086 }
5087 
5088 void
CommandDiceRolls(char * sz)5089 CommandDiceRolls(char *sz)
5090 {
5091     int n;
5092     char *pch;
5093     unsigned int anDice[2];
5094 
5095     if ((pch = NextToken(&sz))) {
5096         n = ParseNumber(&pch);
5097 
5098         while (n-- > 0) {
5099             RollDice(anDice, &rngCurrent, rngctxCurrent);
5100 
5101             printf("%u %u\n", anDice[0], anDice[1]);
5102 
5103         }
5104 
5105     }
5106 }
5107 
5108 
5109 #if defined(HAVE_LIB_READLINE)
5110 extern void
CommandHistory(char * UNUSED (sz))5111 CommandHistory(char *UNUSED(sz))
5112 {
5113 
5114     int i;
5115     HIST_ENTRY *phe;
5116 
5117     for (i = 0; i < history_length; ++i) {
5118         phe = history_get(i + history_base);
5119         outputf("%6d %s\n", i + history_base, phe->line);
5120     }
5121 
5122 
5123 }
5124 
5125 #endif                          /* HAVE_LIB_READLINE */
5126 
5127 extern void
CommandClearHint(char * UNUSED (sz))5128 CommandClearHint(char *UNUSED(sz))
5129 {
5130     pmr_hint_destroy();
5131     outputl(_("Analysis used for `hint' has been cleared"));
5132 }
5133 
5134 
5135 /*
5136  * Description:  Calculate Effective Pip Counts (EPC), either
5137  *               by lookup in one-sided databases or by doing a
5138  *               one-sided rollout.
5139  * Parameters :
5140  *   Input       anBoard (the board)
5141  *               fOnlyRace: only calculate EPCs for race positions
5142  *   Output      arEPC (the calculate EPCs)
5143  *               pfSource (source of EPC; 0 = database, 1 = OSR)
5144  *
5145  * Returns:      0 on success, non-zero otherwise
5146  *
5147  */
5148 
5149 extern int
EPC(const TanBoard anBoard,float * arEPC,float * arMu,float * arSigma,int * pfSource,const int fOnlyBearoff)5150 EPC(const TanBoard anBoard, float *arEPC, float *arMu, float *arSigma, int *pfSource, const int fOnlyBearoff)
5151 {
5152 
5153     const float x = (2 * 3 + 3 * 4 + 4 * 5 + 4 * 6 + 6 * 7 +
5154                      5 * 8 + 4 * 9 + 2 * 10 + 2 * 11 + 1 * 12 + 1 * 16 + 1 * 20 + 1 * 24) / 36.0f;
5155 
5156     if (isBearoff(pbc1, anBoard)) {
5157         /* one sided in-memory database */
5158         float ar[4];
5159         unsigned int n;
5160         int i;
5161 
5162         for (i = 0; i < 2; ++i) {
5163             n = PositionBearoff(anBoard[i], pbc1->nPoints, pbc1->nChequers);
5164 
5165             if (BearoffDist(pbc1, n, NULL, NULL, ar, NULL, NULL))
5166                 return -1;
5167 
5168             if (arEPC)
5169                 arEPC[i] = x * ar[0];
5170             if (arMu)
5171                 arMu[i] = ar[0];
5172             if (arSigma)
5173                 arSigma[i] = ar[1];
5174 
5175         }
5176 
5177         if (pfSource)
5178             *pfSource = 0;
5179 
5180         return 0;
5181 
5182     } else if (isBearoff(pbcOS, anBoard)) {
5183         /* one sided in-memory database */
5184         float ar[4];
5185         unsigned int n;
5186         int i;
5187 
5188         for (i = 0; i < 2; ++i) {
5189             n = PositionBearoff(anBoard[i], pbcOS->nPoints, pbcOS->nChequers);
5190 
5191             if (BearoffDist(pbcOS, n, NULL, NULL, ar, NULL, NULL))
5192                 return -1;
5193 
5194             if (arEPC)
5195                 arEPC[i] = x * ar[0];
5196             if (arMu)
5197                 arMu[i] = ar[0];
5198             if (arSigma)
5199                 arSigma[i] = ar[1];
5200 
5201         }
5202 
5203         if (pfSource)
5204             *pfSource = 0;
5205 
5206         return 0;
5207 
5208     } else if (!fOnlyBearoff) {
5209 
5210         /* one-sided rollout */
5211 
5212         int nTrials = 5760;
5213         float arMux[2];
5214         float ar[5];
5215         int i;
5216 
5217         raceProbs(anBoard, nTrials, ar, arMux);
5218 
5219         for (i = 0; i < 2; ++i) {
5220             if (arEPC)
5221                 arEPC[i] = x * arMux[i];
5222             if (arMu)
5223                 arMu[i] = arMux[i];
5224             if (arSigma)
5225                 arSigma[i] = -1.0f;     /* raceProbs cannot calculate sigma! */
5226         }
5227 
5228         if (pfSource)
5229             *pfSource = 1;
5230 
5231         return 0;
5232 
5233     }
5234 
5235     /* code not reachable */
5236     return -1;
5237 }
5238 
5239 #ifdef WIN32
5240 /* WIN32 setlocale must be manipulated through putenv to be gettext compatible */
5241 char *
SetupLanguage(const char * newLangCode)5242 SetupLanguage(const char *newLangCode)
5243 {
5244     static char *org_lang = NULL;
5245     char *lang;
5246     if (!org_lang)
5247         org_lang = g_win32_getlocale();
5248     if (!newLangCode || !strcmp(newLangCode, "system") || !strcmp(newLangCode, ""))
5249         lang = g_strdup_printf("LANG=%s", org_lang);
5250     else
5251         lang = g_strdup_printf("LANG=%s", newLangCode);
5252     putenv(lang);
5253     g_free(lang);
5254     return (setlocale(LC_ALL, ""));
5255 }
5256 #else
5257 char *
SetupLanguage(const char * newLangCode)5258 SetupLanguage(const char *newLangCode)
5259 {
5260     static char *org_lang = NULL;
5261     char *result = NULL;
5262 
5263     if (!org_lang) {
5264         org_lang = g_strdup(setlocale(LC_ALL, ""));
5265         if (!org_lang) {
5266             outputerrf(_("Locale in your environment not supported by " "C library. Falling back to C locale.\n"));
5267             org_lang = g_strdup("C");
5268         }
5269     }
5270 
5271     if (newLangCode && *newLangCode && strcmp(newLangCode, "system") != 0) {
5272         g_setenv("LC_ALL", newLangCode, TRUE);
5273         result = setlocale(LC_ALL, newLangCode);
5274         return (result);
5275     }
5276     g_setenv("LC_ALL", org_lang, TRUE);
5277     result = setlocale(LC_ALL, org_lang);
5278     return (result);
5279 }
5280 #endif
5281 
5282 void
asyncFindBestMoves(findData * pfd)5283 asyncFindBestMoves(findData * pfd)
5284 {
5285     if (FindnSaveBestMoves(pfd->pml, pfd->anDice[0], pfd->anDice[1], pfd->pboard,
5286                            pfd->keyMove, pfd->rThr, pfd->pci, pfd->pec, pfd->aamf) < 0)
5287         MT_SetResultFailed();
5288 
5289     RefreshMoveList(pfd->pml, NULL);
5290 }
5291 
5292 void
asyncFindMove(findData * pfd)5293 asyncFindMove(findData * pfd)
5294 {
5295     if (FindnSaveBestMoves(pfd->pml, ms.anDice[0], ms.anDice[1], pfd->pboard,
5296                            pfd->keyMove, pfd->rThr, pfd->pci, pfd->pec, pfd->aamf) < 0)
5297         MT_SetResultFailed();
5298 }
5299 
5300 void
asyncDumpDecision(decisionData * pdd)5301 asyncDumpDecision(decisionData * pdd)
5302 {
5303     if (DumpPosition(pdd->pboard, pdd->szOutput, pdd->pec, pdd->pci,
5304                      fOutputMWC, fOutputWinPC, pdd->n, MatchIDFromMatchState(&ms)) != 0)
5305         MT_SetResultFailed();
5306 }
5307 
5308 void
asyncScoreMove(scoreData * psd)5309 asyncScoreMove(scoreData * psd)
5310 {
5311     if (ScoreMove(NULL, psd->pm, psd->pci, psd->pec, psd->pec->nPlies) < 0)
5312         MT_SetResultFailed();
5313 }
5314 
5315 void
asyncEvalRoll(decisionData * pdd)5316 asyncEvalRoll(decisionData * pdd)
5317 {
5318     EvaluateRoll(pdd->aarOutput[0], ms.anDice[0], ms.anDice[1], pdd->pboard, pdd->pci, pdd->pec);
5319 }
5320 
5321 void
asyncAnalyzeMove(moveData * pmd)5322 asyncAnalyzeMove(moveData * pmd)
5323 {
5324     if (AnalyzeMove(pmd->pmr, pmd->pms, plGame, NULL, pmd->pesChequer, pmd->pesCube, pmd->aamf, NULL, NULL) < 0)
5325         MT_SetResultFailed();
5326 }
5327 
5328 void
asyncGammonRates(decisionData * pdd)5329 asyncGammonRates(decisionData * pdd)
5330 {
5331     if (getCurrentGammonRates(pdd->aarRates, pdd->aarOutput[0], pdd->pboard, pdd->pci, pdd->pec) < 0)
5332         MT_SetResultFailed();
5333 }
5334 
5335 void
asyncMoveDecisionE(decisionData * pdd)5336 asyncMoveDecisionE(decisionData * pdd)
5337 {
5338     if (GeneralEvaluationE(pdd->aarOutput[0], pdd->pboard, pdd->pci, pdd->pec) < 0)
5339         MT_SetResultFailed();
5340 }
5341 
5342 void
asyncCubeDecisionE(decisionData * pdd)5343 asyncCubeDecisionE(decisionData * pdd)
5344 {
5345     if (GeneralCubeDecisionE(pdd->aarOutput, pdd->pboard, pdd->pci, pdd->pec, pdd->pes) < 0)
5346         MT_SetResultFailed();
5347 }
5348 
5349 void
asyncCubeDecision(decisionData * pdd)5350 asyncCubeDecision(decisionData * pdd)
5351 {
5352     if (GeneralCubeDecision(pdd->aarOutput, pdd->aarStdDev, pdd->aarsStatistics,
5353                             pdd->pboard, pdd->pci, pdd->pes, NULL, NULL) < 0)
5354         MT_SetResultFailed();
5355 }
5356 
5357 extern int
RunAsyncProcess(AsyncFun fun,void * data,const char * msg)5358 RunAsyncProcess(AsyncFun fun, void *data, const char *msg)
5359 {
5360     int ret;
5361 #if defined(USE_MULTITHREAD)
5362     Task *pt = (Task *) malloc(sizeof(Task));
5363     pt->pLinkedTask = NULL;
5364     pt->fun = fun;
5365     pt->data = data;
5366     MT_AddTask(pt, TRUE);
5367 #endif
5368 
5369     ProgressStart(msg);
5370 
5371 #if defined(USE_MULTITHREAD)
5372     ret = MT_WaitForTasks(Progress, 100, FALSE);
5373 #else
5374     asyncRet = 0;
5375     fun(data);                  /* Just call function in single threaded build */
5376     ret = asyncRet;
5377 #endif
5378 
5379     ProgressEnd();
5380 
5381     return ret;
5382 }
5383 
5384 extern void
ProcessEvents(void)5385 ProcessEvents(void)
5386 {
5387 #if defined(USE_GTK)
5388     if (fX) {
5389         while (gtk_events_pending())
5390             gtk_main_iteration();
5391     } else
5392 #endif
5393     {
5394         while (g_main_context_pending(NULL))
5395             g_main_context_iteration(NULL, TRUE);
5396     }
5397 }
5398 
5399 extern int
CheckGameExists(void)5400 CheckGameExists(void)
5401 {
5402     if (plGame) {
5403         return TRUE;
5404     } else {
5405         outputl(_("No game in progress."));
5406         return FALSE;
5407     }
5408 }
5409 
5410 extern gboolean
save_autosave(gpointer UNUSED (unused))5411 save_autosave(gpointer UNUSED(unused))
5412 {
5413     int fd;
5414     FILE *pf;
5415     listOLD *pl;
5416 
5417     g_return_val_if_fail(plGame, FALSE);
5418 
5419     MT_Exclusive();
5420     if (autosave)
5421         g_unlink(autosave);
5422 
5423     autosave = g_build_filename(szHomeDirectory, "backup", "XXXXXX.sgf", NULL);
5424 
5425     fd = g_mkstemp(autosave);
5426     if (fd < 0) {
5427         g_free(autosave);
5428         autosave = NULL;
5429         MT_Release();
5430         return FALSE;
5431     }
5432 
5433     close(fd);
5434     g_unlink(autosave);
5435 
5436     pf = gnubg_g_fopen(autosave, "w");
5437     if (!pf) {
5438         autosave = NULL;
5439         MT_Release();
5440         return FALSE;
5441     }
5442 
5443     for (pl = lMatch.plNext; pl != &lMatch; pl = pl->plNext)
5444         SaveGame(pf, pl->p);
5445 
5446     fclose(pf);
5447     MT_Release();
5448     return TRUE;
5449 }
5450 
5451 extern void
delete_autosave(void)5452 delete_autosave(void)
5453 {
5454     if (autosave) {
5455         g_unlink(autosave);
5456         g_free(autosave);
5457         autosave = NULL;
5458     }
5459 }
5460 
5461 extern int
get_input_discard(void)5462 get_input_discard(void)
5463 {
5464     if (fInterrupt)
5465         return FALSE;
5466 
5467     if (autosave && fAutoSaveConfirmDelete) {
5468         if (!GetInputYN(_("Are you sure you want to discard the current match and your existing autosave? ")))
5469             return FALSE;
5470         g_unlink(autosave);
5471         g_free(autosave);
5472         autosave = NULL;
5473         return TRUE;
5474     }
5475     if (ms.gs == GAME_PLAYING && fConfirmNew)
5476         return GetInputYN(_("Are you sure you want to discard the current match? "));
5477 
5478     return TRUE;
5479 }
5480