1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2002-2006 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
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.
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
25 #include <sys/types.h>
50 void send_to_engine(const char *format
, ...)
59 len
= vasprintf(&line
, format
, ap
);
61 line
= Malloc(LINE_MAX
);
62 len
= vsnprintf(line
, LINE_MAX
, format
, ap
);
72 FD_SET(enginefd
[1], &fds
);
77 if ((n
= select(enginefd
[1] + 1, NULL
, &fds
, NULL
, &tv
)) > 0) {
78 if (FD_ISSET(enginefd
[1], &fds
)) {
79 n
= write(enginefd
[1], line
, len
);
85 if (kill(enginepid
, 0) == -1) {
86 message(ERROR
, ANYKEY
, "Could not write to engine. "
87 "Process no longer exists.");
88 engine_initialized
= 0;
89 //update_status_window(NULL);
93 message(ERROR
, ANYKEY
, "Attempt #%i. write(): %s", ++try,
99 message(NULL
, ANYKEY
, "try #%i: write() error to engine. "
100 "Expected %i, got %i.", ++try, len
, n
);
117 /* From the Xboard and screen packages. */
118 static int get_pty(char *pty_name
)
123 for (i
= 0; PTY_MAJOR
[i
]; i
++) {
126 for (n
= 0; PTY_MINOR
[n
]; n
++) {
127 sprintf(pty_name
, "%spty%c%c", _PATH_DEV
, PTY_MAJOR
[i
],
130 if ((fd
= open(pty_name
, O_RDWR
| O_NOCTTY
)) == -1)
133 sprintf(pty_name
, "%stty%c%c", _PATH_DEV
, PTY_MAJOR
[i
],
136 if (access(pty_name
, R_OK
| W_OK
) == -1) {
149 static char **parseargs(char *str
)
161 if (!(pptr
= malloc(sizeof(char *))))
164 for (i
= 0, s
= str
; *s
; lastchar
= *s
++) {
165 if ((*s
== '\"' || *s
== '\'') && lastchar
!= '\\') {
166 quote
= (quote
) ? 0 : 1;
170 if (*s
== ' ' && !quote
) {
172 pptr
= realloc(pptr
, (n
+ 2) * sizeof(char *));
173 pptr
[n
++] = strdup(arg
);
178 if ((i
+ 1) == sizeof(arg
))
187 pptr
= realloc(pptr
, (n
+ 2) * sizeof(char *));
188 pptr
[n
++] = strdup(arg
);
195 /* Is this dangerous if pty permissions are wrong? */
196 pid_t
init_chess_engine(char **args
)
201 char pty
[FILENAME_MAX
];
203 if ((to
[1] = get_pty(pty
)) == -1) {
208 if ((to
[1] = open("/dev/ptmx", O_RDWR
| O_NOCTTY
)) == -1) {
212 if (grantpt(to
[1]) == -1) {
216 if (unlockpt(to
[1]) == -1) {
225 if ((to
[0] = open(pty
, O_RDWR
| O_NOCTTY
)) == -1)
227 if ((to
[0] = open(ptsname(to
[1]), O_RDWR
| O_NOCTTY
)) == -1)
234 switch ((pid
= fork())) {
238 dup2(to
[0], STDIN_FILENO
);
239 dup2(from
[1], STDOUT_FILENO
);
244 dup2(STDOUT_FILENO
, STDERR_FILENO
);
245 execvp(args
[0], args
);
253 if (kill(pid
, 0) == -1)
258 enginefd
[0] = from
[0];
260 fcntl(enginefd
[0], F_SETFL
, O_NONBLOCK
| O_DIRECT
);
261 fcntl(enginefd
[1], F_SETFL
, O_NONBLOCK
| O_DIRECT
);
262 engine_initialized
= 1;
266 void set_engine_defaults()
272 if (!engine_initialized
)
275 SEND_TO_ENGINE("quit\n");
277 if (kill(enginepid
, 0) != -1)
278 kill(enginepid
, SIGTERM
);
280 if (kill(enginepid
, 0) != -1)
281 kill(enginepid
, SIGKILL
);
286 int start_chess_engine()
291 args
= parseargs(config
.engine_cmd
);
292 enginepid
= init_chess_engine(args
);
296 /* Pty allocation. */
297 message(ERROR
, ANYKEY
, "Could not allocate PTY");
300 /* Could not execute engine. */
301 message(ERROR
, ANYKEY
, "%s: %s", args
[0], strerror(errno
));
307 for (i
= 0; args
[i
]; i
++)
313 set_engine_defaults();
315 //update_status_window();
319 /* Once the PGN parser has been well tested, validate_move() from the human
320 * move can disappear.
322 void parse_gnuchess_line(GAME
*g
, char *str
)
324 char m
[MAX_SAN_MOVE_LEN
+ 1] = {0}, *p
= m
;
327 /* Human move. Add it to the move history. */
328 if (sscanf(str
, "%*d%*1[.]%*1[ ]%[a-zA-Z0-9+=#-]%n", m
, &count
) == 1) {
330 if (validate_move(g, m)) {
336 if (history_total(g
->hp
) == 0 && g
->side
== BLACK
)
337 SET_FLAG(g
->flags
, GF_BLACK_OPENING
);
340 SET_FLAG(g
->flags
, GF_MODIFIED
);
347 if (sscanf(str
, "%*d%*1[.]%*1[ ]%*3[.]%*1[ ]%[a-zA-Z0-9+=#-]%n", m
,
349 /* Moves from the engine are in a2a4 format (Xboard protocol) so we
350 * need to convert them.
354 if ((p = pgn_a2a4tosan(g, m)) == NULL)
359 if (validate_move(g, p)) {
365 if (history_total(g
->hp
) == 0 && g
->side
== BLACK
)
366 SET_FLAG(g
->flags
, GF_BLACK_OPENING
);
369 SET_FLAG(g
->flags
, GF_MODIFIED
);
373 if (TEST_FLAG(g
->flags
, GF_GAMEOVER
)) {
374 //history_update_board(g, g.htotal); FIXME
381 if (TEST_FLAG(g
->flags
, GF_GAMEOVER
)) {
382 //history_update_board(g); FIXME
386 /* Miscellaneous one-liners. */
388 /* The engine is now reading a FIFO. Dump what we need to it. */
390 if (strncmp(str
, "pgnload ", 8) == 0) {
391 if (save_pgn(config
.fifo
, 1, gindex
)) {
396 history_free(g
->hp
, g
->hindex
+ 1);
397 CLEAR_FLAG(g
->flags
, GF_GAMEOVER
);
398 SET_FLAG(g
->flags
, GF_MODIFIED
);
401 set_engine_defaults();
405 /* 'switch' command. */
406 if (strncmp(str
, "White to move", 13) == 0) {
407 g
->side
= g
->turn
= WHITE
;
410 else if (strncmp(str
, "Black to move", 13) == 0) {
411 g
->side
= g
->turn
= BLACK
;
415 /* Bad engine command or move. */
416 if (strncmp(str
, "Illegal move: ", 14) == 0) {
421 static void parse_engine_line(GAME
*g
, char *line
)
428 parse_gnuchess_line(g
, line
);
431 void parse_engine_output(GAME
*g
, char *str
)
433 char buf
[LINE_MAX
], *p
= buf
;
438 /* FIXME test this ("White ... : ", "Black ... : "). Needed for the
448 parse_engine_line(g
, buf
);