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
23 #include <sys/types.h>
25 #include <sys/socket.h>
52 if (n
== -1 || !config
.agony
|| !curses_initialized
||
53 (status
.mode
== MODE_HISTORY
&& !config
.historyagony
))
57 if ((fp
= fopen(config
.agonyfile
, "r")) == NULL
) {
59 cmessage(ERROR
, ANYKEY
, "%s: %s", config
.agonyfile
, strerror(errno
));
64 if (fscanf(fp
, " %[^\n] ", line
) == 1) {
65 agony
= Realloc(agony
, (n
+ 2) * sizeof(char *));
66 agony
[n
++] = strdup(trim(line
));
73 if (agony
[0] == NULL
|| !n
) {
79 return agony
[random() % n
];
82 void draw_board(BOARD b
, int crow
, int ccol
)
85 int bcol
= 0, brow
= 0;
86 int maxy
= BOARD_HEIGHT
, maxx
= BOARD_WIDTH
;
87 int ncols
= 0, offset
= 1;
88 unsigned coords_y
= 8;
90 for (row
= 0; row
< maxy
; row
++) {
93 for (col
= 0; col
< maxx
; col
++) {
96 chtype piece
, movecount
= 0;
99 if (row
== 0 || row
== maxy
- 2) {
101 mvwaddch(boardw
, row
, col
,
103 ACS_LLCORNER
| CP_BOARD_GRAPHICS
:
104 ACS_ULCORNER
| CP_BOARD_GRAPHICS
));
105 else if (col
== maxx
- 2)
106 mvwaddch(boardw
, row
, col
,
108 ACS_LRCORNER
| CP_BOARD_GRAPHICS
:
109 ACS_URCORNER
| CP_BOARD_GRAPHICS
));
111 mvwaddch(boardw
, row
, col
,
113 ACS_BTEE
| CP_BOARD_GRAPHICS
:
114 ACS_TTEE
| CP_BOARD_GRAPHICS
));
117 mvwaddch(boardw
, row
, col
,
118 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
124 if ((row
% 2) && col
== maxx
- 1 && coords_y
) {
125 wattron(boardw
, CP_BOARD_COORDS
);
126 mvwprintw(boardw
, row
, col
, "%d", coords_y
--);
127 wattroff(boardw
, CP_BOARD_COORDS
);
131 if ((col
== 0 || col
== maxx
- 2) && row
!= maxy
- 1) {
133 mvwaddch(boardw
, row
, col
,
135 ACS_RTEE
| CP_BOARD_GRAPHICS
:
136 ACS_LTEE
| CP_BOARD_GRAPHICS
));
138 mvwaddch(boardw
, row
, col
,
139 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
144 if ((row
% 2) && !(col
% 4) && row
!= maxy
- 1) {
145 mvwaddch(boardw
, row
, col
,
146 LINE_GRAPHIC(ACS_VLINE
| CP_BOARD_GRAPHICS
));
150 if (!(col
% 4) && row
!= maxy
- 1) {
151 mvwaddch(boardw
, row
, col
,
152 LINE_GRAPHIC(ACS_PLUS
| CP_BOARD_GRAPHICS
));
163 if (((ncols
% 2) && !(offset
% 2)) || (!(ncols
% 2)
169 if (config
.validmoves
&& b
[brow
][bcol
].valid
) {
170 attrs
= (attrwhich
== WHITE
) ? CP_BOARD_MOVES_WHITE
:
171 CP_BOARD_MOVES_BLACK
;
173 if (b
[brow
][bcol
].movecount
) {
174 if (brow
+ 1 != crow
&& bcol
+ 1 != ccol
)
175 movecount
= (b
[brow
][bcol
].movecount
+ '0');
179 attrs
= (attrwhich
== WHITE
) ? CP_BOARD_WHITE
:
182 if (row
== ROWTOMATRIX(crow
) && col
== COLTOMATRIX(ccol
)) {
183 attrs
= CP_BOARD_CURSOR
;
186 if (row
== ROWTOMATRIX(sp
.row
) &&
187 col
== COLTOMATRIX(sp
.col
)) {
188 attrs
= CP_BOARD_SELECTED
;
194 mvwaddch(boardw
, row
, col
, ' ' | attrs
);
197 waddch(boardw
, x_grid_chars
[bcol
] | CP_BOARD_COORDS
);
199 piece
= b
[row
/ 2][bcol
].icon
;
204 if (status
.side
== WHITE
&& isupper(piece
))
206 else if (status
.side
== BLACK
&& islower(piece
))
210 (piece
&& piece
!= int_to_piece(OPEN_SQUARE
)) ?
211 piece
| attrs
: ' ' | attrs
);
217 if (movecount
&& row
!= maxy
-1)
218 waddch(boardw
, movecount
| CP_BOARD_COUNT
);
220 waddch(boardw
, ' ' | attrs
);
228 mvwaddch(boardw
, row
, col
,
229 LINE_GRAPHIC(ACS_HLINE
| CP_BOARD_GRAPHICS
));
239 /* Convert the selected piece to SAN format and validate it. */
240 static char *board_to_san(BOARD b
)
242 static char str
[MAX_PGN_MOVE_LEN
+ 1], *p
;
247 snprintf(str
, sizeof(str
), "%c%i%c%i", x_grid_chars
[sp
.col
- 1], sp
.row
,
248 x_grid_chars
[sp
.destcol
- 1], sp
.destrow
);
251 piece
= piece_to_int(b
[ROWTOBOARD(sp
.row
)][COLTOBOARD(sp
.col
)].icon
);
253 if (piece
== PAWN
&& ((sp
.destrow
== 8 && status
.turn
== WHITE
) ||
254 (sp
.destrow
== 1 && status
.turn
== BLACK
))) {
255 promo
= cmessage(PROMOTION_TITLE
, PROMOTION_PROMPT
, PROMOTION_TEXT
);
257 if (piece_to_int(promo
) == -1)
260 p
= str
+ strlen(str
);
261 *p
++ = toupper(promo
);
265 memcpy(t
, b
, sizeof(BOARD
));
267 if ((p
= a2a4tosan(t
, str
)) == NULL
) {
268 cmessage(p
, ANYKEY
, "%s", E_A2A4_PARSE
);
272 if (parse_move_text(t
, p
)) {
277 memcpy(b
, t
, sizeof(BOARD
));
281 static int move_to_engine(BOARD b
)
285 if ((p
= board_to_san(b
)) == NULL
)
288 sp
.row
= sp
.col
= sp
.icon
= 0;
291 add_to_history(&game
[gindex
].history
, &game
[gindex
].hindex
,
292 &game
[gindex
].htotal
, p
);
294 SET_FLAG(game
[gindex
].flags
, GF_MODIFIED
);
299 SEND_TO_ENGINE("%s\n", p
);
303 char *book_method(int method
)
309 book
= BOOK_BEST_STR
;
312 book
= BOOK_WORST_STR
;
315 book
= BOOK_PREFER_STR
;
318 book
= BOOK_RANDOM_STR
;
331 static void update_clock(int n
, int *h
, int *m
, int *s
)
334 *m
= (n
% 3600) / 60;
335 *s
= (n
% 3600) % 60;
340 void update_status_window()
343 char buf
[STATUS_WIDTH
- 7];
344 char tmp
[15], *engine
, *mode
;
345 int w
= STATUS_WIDTH
- 10;
352 if (TEST_FLAG(game
[gindex
].flags
, GF_DELETE
)) {
358 if (TEST_FLAG(game
[gindex
].flags
, GF_PERROR
)) {
368 if (TEST_FLAG(game
[gindex
].flags
, GF_MODIFIED
)) {
383 mvwprintw(statusw
, 2, 1, "%*s %-*s", 7, STATUS_FILE_STR
, w
,
384 (loadfile
[0]) ? str_etc(loadfile
, w
, 1) : UNAVAILABLE
);
385 snprintf(buf
, sizeof(buf
), "%i %s %i %s", gindex
+ 1, N_OF_N_STR
, gtotal
,
387 mvwprintw(statusw
, 3, 1, "%*s %-*s", 7, STATUS_GAME_STR
, w
, buf
);
389 switch (status
.mode
) {
391 mode
= MODE_HISTORY_STR
;
394 mode
= MODE_EDIT_STR
;
397 mode
= MODE_PLAY_STR
;
404 mvwprintw(statusw
, 4, 1, "%*s %-*s", 7, STATUS_MODE_STR
, w
, mode
);
406 switch (status
.engine
) {
407 case ENGINE_THINKING
:
408 engine
= ENGINE_THINKING_STR
;
411 engine
= ENGINE_READY_STR
;
413 case ENGINE_INITIALIZING
:
414 engine
= ENGINE_INITIALIZING_STR
;
417 engine
= ENGINE_OFFLINE_STR
;
424 mvwprintw(statusw
, 5, 1, "%*s %-*s", 7, STATUS_ENGINE_STR
, w
, " ");
425 wattron(statusw
, CP_STATUS_ENGINE
);
426 mvwaddstr(statusw
, 5, 9, engine
);
427 wattroff(statusw
, CP_STATUS_ENGINE
);
429 mvwprintw(statusw
, 6, 1, "%*s %-*i", 7, STATUS_DEPTH_STR
, w
,
430 config
.engine_depth
);
432 mvwprintw(statusw
, 7, 1, "%*s %-*s", 7, STATUS_BOOK_STR
, w
,
433 book_method(config
.book_method
));
435 mvwprintw(statusw
, 8, 1, "%*s %-*s", 7, STATUS_TURN_STR
, w
,
436 (status
.turn
== WHITE
) ? WHITE_STR
: BLACK_STR
);
438 strncpy(tmp
, WHITE_STR
, sizeof(tmp
));
439 tmp
[0] = toupper(tmp
[0]);
440 update_clock(game
[gindex
].moveclock
, &h
, &m
, &s
);
441 snprintf(buf
, sizeof(buf
), "c/%-2i %.2i:%.2i:%.2i", game
[gindex
].wcaptures
,
443 mvwprintw(statusw
, 9, 1, "%*s: %-*s", 6, tmp
, w
, buf
);
445 strncpy(tmp
, BLACK_STR
, sizeof(tmp
));
446 tmp
[0] = toupper(tmp
[0]);
447 update_clock(game
[gindex
].moveclock
, &h
, &m
, &s
);
448 snprintf(buf
, sizeof(buf
), "c/%-2i %.2i:%.2i:%.2i", game
[gindex
].bcaptures
,
450 mvwprintw(statusw
, 10, 1, "%*s: %-*s", 6, tmp
, w
, buf
);
452 for (i
= 1; i
< STATUS_WIDTH
- 4; i
++)
453 mvwprintw(statusw
, STATUS_HEIGHT
- 2, i
, " ");
456 status
.notify
= strdup(GAME_HELP_PROMPT
);
458 wattron(statusw
, CP_STATUS_NOTIFY
);
459 mvwprintw(statusw
, STATUS_HEIGHT
- 2,
460 CENTERX(STATUS_WIDTH
, status
.notify
), "%s", status
.notify
);
461 wattroff(statusw
, CP_STATUS_NOTIFY
);
464 void update_history_window()
466 char buf
[HISTORY_WIDTH
];
467 HISTORY h
= {{0},NULL
,{0}};
470 n
= (game
[gindex
].hindex
+ 1) / 2;
472 if ((game
[gindex
].htotal
% 2))
473 total
= (game
[gindex
].htotal
+ 1) / 2;
475 total
= game
[gindex
].htotal
/ 2;
477 if (game
[gindex
].htotal
)
478 snprintf(buf
, sizeof(buf
), "%u %s %u%s",n
, N_OF_N_STR
, total
,
479 (movestep
== 1) ? HISTORY_MOVE_STEP
: "");
481 strncpy(buf
, UNAVAILABLE
, sizeof(buf
));
483 mvwprintw(historyw
, 2, 1, "%*s %-*s", 10, HISTORY_MOVE_STR
,
484 HISTORY_WIDTH
- 13, buf
);
486 if (get_history_by_index(game
[gindex
].hindex
, &h
))
487 memset(&h
, 0, sizeof(HISTORY
));
489 snprintf(buf
, sizeof(buf
), "%s %s", (h
.move
[0]) ? h
.move
: UNAVAILABLE
,
490 ((h
.comment
&& h
.comment
[0]) || h
.nag
[0]) ? HISTORY_ANNO_NEXT
: "");
491 mvwprintw(historyw
, 3, 1, "%s %-*s", HISTORY_MOVE_NEXT_STR
,
492 HISTORY_WIDTH
- 13, buf
);
494 if (get_history_by_index(game
[gindex
].hindex
- 1, &h
))
495 memset(&h
, 0, sizeof(HISTORY
));
497 snprintf(buf
, sizeof(buf
), "%s %s", (h
.move
[0]) ? h
.move
: UNAVAILABLE
,
498 ((h
.comment
&& h
.comment
[0]) || h
.nag
[0]) ? HISTORY_ANNO_PREV
: "");
499 mvwprintw(historyw
, 4, 1, "%s %-*s", HISTORY_MOVE_PREV_STR
,
500 HISTORY_WIDTH
- 13, buf
);
504 void update_tag_window()
507 int w
= TAG_WIDTH
- 10;
509 for (i
= 0; i
< 7; i
++) {
510 char *value
= game
[gindex
].tag
[i
].value
;
513 if ((*value
== '?' || *value
== '-') && value
[1] == '\0')
515 else if (strcmp(game
[gindex
].tag
[i
].name
, "Result") == 0) {
516 for (n
= 0; n
< NARRAY(fancy_results
); n
++) {
517 if (strcmp(value
, fancy_results
[n
].pgn
) == 0) {
518 value
= fancy_results
[n
].fancy
;
524 value
= str_etc(value
, w
, 0);
526 mvwprintw(tagw
, (i
+ 2), 1, "%*s: %-*s", 6, game
[gindex
].tag
[i
].name
,
533 void draw_prompt(WINDOW
*win
, int y
, int width
, const char *str
, chtype attr
)
539 for (i
= 1; i
< width
- 1; i
++)
540 mvwaddch(win
, y
, i
, ' ');
542 mvwprintw(win
, y
, CENTERX(width
, str
), "%s", str
);
548 void draw_window_title(WINDOW
*win
, const char *title
, int width
, chtype attr
,
556 for (i
= 1; i
< width
- 1; i
++)
557 mvwaddch(win
, 1, i
, ' ');
559 mvwprintw(win
, 1, CENTERX(width
, title
), "%s", title
);
564 box(win
, ACS_VLINE
, ACS_HLINE
);
565 wattroff(win
, battr
);
572 update_status_window();
573 update_history_window();
577 static void game_next_prev(int n
, int count
)
583 if (gindex
+ count
> gtotal
- 1) {
593 if (gindex
- count
< 0) {
609 void free_game_data()
616 for (i
= 0; i
< gtotal
; i
++) {
617 free_historydata(&game
[i
].history
, 0, game
[i
].htotal
);
618 free(game
[i
].history
);
619 free_tag_data(game
[i
].tag
, game
[i
].tindex
);
626 static void delete_game(int which
)
632 for (i
= 0; i
< gtotal
; i
++) {
633 if (i
== which
|| TEST_FLAG(game
[i
].flags
, GF_DELETE
)) {
634 free_historydata(&game
[i
].history
, 0, game
[i
].htotal
);
635 free(game
[i
].history
);
636 free_tag_data(game
[i
].tag
, game
[i
].tindex
);
641 g
= Realloc(g
, (gi
+ 2) * sizeof(GAME
));
643 memcpy(&g
[gi
], &game
[i
], sizeof(GAME
));
645 g
[gi
].tag
= game
[i
].tag
;
646 g
[gi
].history
= game
[i
].history
;
654 if (which
+ 1 >= gtotal
)
665 /* FIXME dont show out of reach counts. Diagonals. */
667 static void number_valid_moves(BOARD b, int srow, int scol)
672 for (row = srow + 1, col = scol, count = 1; VALIDFILE(row); row++) {
673 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
676 b[ROWTOBOARD(row)][COLTOBOARD(col)].movecount = count++;
679 for (row = srow - 1, col = scol, count = 1; VALIDFILE(row); row--) {
680 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
683 b[ROWTOBOARD(row)][COLTOBOARD(col)].movecount = count++;
686 for (col = scol + 1, row = srow, count = 1; VALIDFILE(col); col++) {
687 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
690 b[ROWTOBOARD(row)][COLTOBOARD(col)].movecount = count++;
693 for (col = scol - 1, row = srow, count = 1; VALIDFILE(col); col--) {
694 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
697 b[ROWTOBOARD(row)][COLTOBOARD(col)].movecount = count++;
705 static void get_valid_cursor(BOARD b, int which, int count, int *crow,
706 int *ccol, int minr, int maxr, int minc, int maxc)
711 if (which == UP || which == RIGHT)
727 if (!VALIDFILE(*crow))
731 for (row = *crow + incr, col = *ccol; VALIDFILE(row); row += incr) {
732 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
750 if (!VALIDFILE(*ccol))
754 for (col = *ccol + incr, row = *crow; VALIDFILE(col); col += incr) {
755 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
767 if (*ccol < sp.col || (which == DOWN || which == LEFT))
772 if (*ccol < sp.col && (which == DOWN || which == LEFT))
775 for (row = *crow + incr; VALIDFILE(row); row += incr) {
776 for (col = *ccol + cincr; VALIDFILE(col); col += cincr) {
777 if (!b[ROWTOBOARD(row)][COLTOBOARD(col)].valid)
787 number_valid_moves(board, *crow, *ccol);
792 static int find_move_exp(const char *str
, int init
, int which
, int count
)
797 static int firstrun
= 1;
806 if ((ret
= regcomp(&r
, str
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
807 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
808 cmessage(E_REGCOMP_TITLE
, ANYKEY
, "%s", errbuf
);
815 incr
= (which
== 0) ? -(1) : 1;
817 for (i
= game
[gindex
].hindex
+ incr
- 1, found
= 0; ; i
+= incr
) {
818 if (i
== game
[gindex
].hindex
- 1)
821 if (i
> game
[gindex
].htotal
)
824 i
= game
[gindex
].htotal
;
826 ret
= regexec(&r
, game
[gindex
].history
[i
].move
, 0, 0, 0);
829 if (count
== ++found
) {
834 if (ret
!= REG_NOMATCH
) {
835 regerror(ret
, &r
, errbuf
, sizeof(errbuf
));
836 cmessage(E_REGEXEC_TITLE
, ANYKEY
, "%s", errbuf
);
845 static int toggle_delete_flag(int n
)
849 if (TEST_FLAG(game
[n
].flags
, GF_DELETE
))
850 CLEAR_FLAG(game
[n
].flags
, GF_DELETE
);
852 SET_FLAG(game
[n
].flags
, GF_DELETE
);
855 for (i
= x
= 0; i
< gtotal
; i
++) {
856 if (TEST_FLAG(game
[i
].flags
, GF_DELETE
))
861 cmessage(NULL
, ANYKEY
, "%s", E_DELETE_GAME
);
862 CLEAR_FLAG(game
[n
].flags
, GF_DELETE
);
869 static void edit_save_tags(int n
)
874 if ((t
= edit_tags(board
, game
[n
].tag
, game
[n
].tindex
, 1)) == NULL
)
879 for (i
= 0; t
[i
].name
; i
++) {
880 add_tag(&game
[n
].tag
, &game
[n
].tindex
, t
[i
].name
, t
[i
].value
);
885 SET_FLAG(game
[n
].flags
, GF_MODIFIED
);
889 static int find_game_exp(char *str
, int which
, int count
)
891 char *nstr
= NULL
, *exp
= NULL
;
898 int incr
= (which
== 0) ? -(1) : 1;
900 strncpy(buf
, str
, sizeof(buf
));
903 if (strstr(tmp
, ":") != NULL
) {
904 nstr
= strsep(&tmp
, ":");
906 if ((ret
= regcomp(&nexp
, nstr
,
907 REG_ICASE
|REG_EXTENDED
|REG_NOSUB
)) != 0) {
908 regerror(ret
, &nexp
, errbuf
, sizeof(errbuf
));
909 cmessage(E_REGCOMP_TITLE
, ANYKEY
, "%s", errbuf
);
920 if ((ret
= regcomp(&vexp
, exp
, REG_EXTENDED
|REG_NOSUB
)) != 0) {
921 regerror(ret
, &vexp
, errbuf
, sizeof(errbuf
));
922 cmessage(E_REGCOMP_TITLE
, ANYKEY
, "%s", errbuf
);
929 for (g
= gindex
+ incr
, found
= 0; ; g
+= incr
) {
940 for (t
= 0; t
< game
[g
].tindex
; t
++) {
942 if (regexec(&nexp
, game
[g
].tag
[t
].name
, 0, 0, 0) == 0) {
943 if (regexec(&vexp
, game
[g
].tag
[t
].value
, 0, 0, 0) == 0) {
944 if (count
== ++found
) {
952 if (regexec(&vexp
, game
[g
].tag
[t
].value
, 0, 0, 0) == 0) {
953 if (count
== ++found
) {
974 void edit_board(BOARD b
)
978 p
= b
[ROWTOBOARD(sp
.row
)][COLTOBOARD(sp
.col
)].icon
;
979 b
[ROWTOBOARD(sp
.destrow
)][COLTOBOARD(sp
.destcol
)].icon
= p
;
980 b
[ROWTOBOARD(sp
.row
)][COLTOBOARD(sp
.col
)].icon
=
981 int_to_piece(OPEN_SQUARE
);
986 // Updates the notification line in the status window then refreshes the
988 void update_status_notify(char *fmt
, ...)
991 #ifdef HAVE_VASPRINTF
1000 status
.notify
= NULL
;
1002 if (curses_initialized
)
1003 update_status_window();
1010 #ifdef HAVE_VASPRINTF
1011 vasprintf(&line
, fmt
, ap
);
1013 vsnprintf(line
, sizeof(line
), fmt
, ap
);
1018 free(status
.notify
);
1020 status
.notify
= strdup(line
);
1022 #ifdef HAVE_VASPRINTF
1025 if (curses_initialized
)
1026 update_status_window();
1029 static void switch_side()
1031 if (status
.side
== WHITE
)
1032 status
.side
= BLACK
;
1034 status
.side
= WHITE
;
1039 int error_recover
= 0;
1042 int crow
= 2, ccol
= 5;
1043 char moveexp
[255] = {0};
1044 char gameexp
[255] = {0};
1045 int delete_count
= 0;
1046 int markstart
= -1, markend
= -1;
1049 gindex
= gtotal
- 1;
1050 markstart
= -1, markend
= -1;
1053 init_history(board
);
1055 update_status_notify("%s", GAME_HELP_PROMPT
);
1057 paused
= 1; //FIXME clock
1060 update_tag_window();
1065 int i
, x
, n
= 0, len
= 0;
1066 char fdbuf
[8192] = {0};
1070 char tfile
[FILENAME_MAX
];
1071 int minr
, maxr
, minc
, maxc
;
1073 if (engine_initialized
) {
1078 FD_SET(enginefd
[0], &fds
);
1080 for (i
= 0; i
< gtotal
; i
++) {
1081 if (game
[i
].sockfd
> 0) {
1082 if (game
[i
].sockfd
> n
)
1085 FD_SET(game
[i
].sockfd
, &fds
);
1089 n
= (n
> enginefd
[0]) ? n
: enginefd
[0];
1091 if ((n
= select(n
+ 1, &fds
, NULL
, NULL
, &tv
)) > 0) {
1092 if (FD_ISSET(enginefd
[0], &fds
)) {
1093 len
= read(enginefd
[0], fdbuf
, sizeof(fdbuf
));
1096 if (errno
!= EAGAIN
) {
1097 cmessage(ERROR
, ANYKEY
, "Attempt #%i. read(): %s",
1098 ++error_recover
, strerror(errno
));
1104 parse_engine_output(board
, fdbuf
);
1110 for (i
= 0; i
< gtotal
; i
++) {
1111 if (game
[i
].sockfd
<= 0)
1114 if (FD_ISSET(game
[i
].sockfd
, &fds
)) {
1115 len
= recv(game
[i
].sockfd
, fdbuf
, sizeof(fdbuf
), 0);
1118 if (errno
!= EAGAIN
) {
1119 cmessage(ERROR
, ANYKEY
,
1120 "Attempt #%i. recv(): %s",
1121 ++error_recover
, strerror(errno
));
1127 parse_ics_output(fdbuf
);
1136 cmessage(ERROR
, ANYKEY
, "select(): %s", strerror(errno
));
1144 draw_board(board
, crow
, ccol
);
1146 wmove(boardw
, ROWTOMATRIX(crow
), COLTOMATRIX(ccol
));
1157 if ((c
= wgetch(boardw
)) == ERR
)
1161 if (!count
&& status
.notify
)
1162 update_status_notify(NULL
);
1175 if (game
[gindex
].htotal
)
1180 add_tag(&game
[gindex
].tag
, &game
[gindex
].tindex
,
1181 "FEN", board_to_fen(board
, game
[gindex
]));
1182 add_tag(&game
[gindex
].tag
, &game
[gindex
].tindex
,
1184 status
.mode
= MODE_PLAY
;
1185 game
[gindex
].fentag
= game
[gindex
].tindex
- 1;
1188 status
.mode
= MODE_EDIT
;
1200 if (!*gameexp
|| c
== '?') {
1201 if ((tmp
= get_input(GAME_FIND_EXPRESSION_TITLE
, gameexp
,
1202 1, 1, GAME_FIND_EXPRESSION_PROMPT
, NULL
,
1203 NULL
, 0, -1)) == NULL
)
1206 strncpy(gameexp
, tmp
, sizeof(gameexp
));
1209 if ((n
= find_game_exp(gameexp
, (c
== '{') ? 0 : 1,
1210 (count
) ? count
: 1)) == -1)
1214 init_history(board
);
1216 update_tag_window();
1268 if (status
.engine
!= ENGINE_READY
)
1271 n
= (count
) ? count
: 1;
1274 if (config
.engine_depth
- n
< 0)
1277 n
-= config
.engine_depth
;
1280 n
+= config
.engine_depth
;
1282 SEND_TO_ENGINE("depth %i\n", abs(n
));
1287 if (game
[gindex
].htotal
< 2)
1292 if (!*moveexp
|| c
== '/') {
1293 if ((tmp
= get_input(FIND_REGEXP
, moveexp
, 1, 1, NULL
,
1294 NULL
, NULL
, 0, -1)) == NULL
)
1297 strncpy(moveexp
, tmp
, sizeof(moveexp
));
1301 if ((n
= find_move_exp(moveexp
, n
, (c
== '[') ? 0 : 1,
1302 (count
) ? count
: 1)) == -1)
1305 game
[gindex
].hindex
= n
;
1306 parse_history_move(board
, game
[gindex
].hindex
);
1310 view_annotation(game
[gindex
].hindex
);
1313 view_annotation(game
[gindex
].hindex
- 1);
1316 game_next_prev(1, (count
) ? count
: 1);
1324 status
.mode
= MODE_HISTORY
;
1328 game_next_prev(0, (count
) ? count
: 1);
1336 status
.mode
= MODE_HISTORY
;
1340 if (status
.mode
!= MODE_HISTORY
|| game
[gindex
].htotal
< 2)
1344 if ((tmp = get_input(GAME_HISTORY_JUMP_TITLE, NULL, 1, 1,
1345 NULL, NULL, NULL, 0, FIELD_TYPE_INTEGER, 1, 0,
1346 game[gindex].htotal)) == NULL)
1351 if ((tmp
= get_input(GAME_HISTORY_JUMP_TITLE
, NULL
, 1, 1,
1352 NULL
, NULL
, NULL
, 0, -1)) == NULL
)
1355 if (!isinteger(tmp
))
1363 if (i
> (game
[gindex
].htotal
/ 2) || i
< 0)
1366 game
[gindex
].hindex
= i
* 2;
1367 init_history(board
);
1375 if ((tmp = get_input(GAME_JUMP_TITLE, NULL, 1, 1, NULL, NULL,
1376 NULL, 0, FIELD_TYPE_INTEGER, 1, 1, gtotal))
1382 if ((tmp
= get_input(GAME_JUMP_TITLE
, NULL
, 1, 1, NULL
,
1383 NULL
, NULL
, 0, -1)) == NULL
)
1386 if (!isinteger(tmp
))
1394 if (--i
> gtotal
- 1 || i
< 0)
1398 init_history(board
);
1400 update_tag_window();
1407 board
[ROWTOBOARD(sp
.row
)][COLTOBOARD(sp
.col
)].icon
=
1408 int_to_piece(OPEN_SQUARE
);
1410 board
[ROWTOBOARD(crow
)][COLTOBOARD(ccol
)].icon
=
1411 int_to_piece(OPEN_SQUARE
);
1413 sp
.icon
= sp
.row
= sp
.col
= 0;
1420 if (count
&& !delete_count
) {
1423 update_status_notify("%s (delete)", status
.notify
);
1427 if (markstart
>= 0 && markend
>= 0) {
1428 if (markstart
> markend
) {
1430 markstart
= markend
;
1434 for (i
= markstart
; i
<= markend
; i
++) {
1435 if (toggle_delete_flag(i
))
1440 if (toggle_delete_flag(gindex
))
1444 markstart
= markend
= -1;
1445 update_status_window();
1449 cmessage(NULL
, ANYKEY
, "%s", E_DELETE_GAME
);
1455 for (i
= n
= 0; i
< gtotal
; i
++) {
1456 if (TEST_FLAG(game
[i
].flags
, GF_DELETE
))
1461 tmp
= GAME_DELETE_GAME_TEXT
;
1464 cmessage(NULL
, ANYKEY
, "%s", E_DELETE_GAME
);
1468 tmp
= GAME_DELETE_ALL_TEXT
;
1471 if (config
.deleteprompt
) {
1472 if ((c
= cmessage(NULL
, YESNO
, "%s", tmp
)) != 'y')
1476 delete_game((!n
) ? gindex
: -1);
1477 init_history(board
);
1479 update_tag_window();
1482 annotate
= game
[gindex
].hindex
;
1484 if (annotate
&& game
[gindex
].history
[annotate
- 1].move
[0])
1489 snprintf(buf
, sizeof(buf
), "%s \"%s\"", ANNOTATION_EDIT_TITLE
,
1490 game
[gindex
].history
[annotate
].move
);
1492 tmp
= get_input(buf
, game
[gindex
].history
[annotate
].comment
,
1493 0, 0, NAG_PROMPT
, history_edit_nag
, (void *)annotate
,
1496 if (!tmp
&& (!game
[gindex
].history
[annotate
].comment
||
1497 !*game
[gindex
].history
[annotate
].comment
))
1499 else if (tmp
&& game
[gindex
].history
[annotate
].comment
) {
1500 if (strcmp(tmp
, game
[gindex
].history
[annotate
].comment
)
1505 len
= (tmp
) ? strlen(tmp
) + 1 : 1;
1507 game
[gindex
].history
[annotate
].comment
=
1508 Realloc(game
[gindex
].history
[annotate
].comment
, len
);
1510 strncpy(game
[gindex
].history
[annotate
].comment
,
1511 (tmp
) ? tmp
: "", len
);
1513 SET_FLAG(game
[gindex
].flags
, GF_MODIFIED
);
1518 edit_save_tags(gindex
);
1520 update_tag_window();
1526 c
= message(GAME_EDIT_TITLE
, GAME_EDIT_PROMPT
, "%s",
1529 if (piece_to_int(c
) == -1 && tolower(c
) != 'x')
1532 if (tolower(c
) == 'x')
1535 if (c
== 'x' && (crow
!= 6 && crow
!= 3))
1539 for (i
= 0; i
< 8; i
++) {
1540 if (board
[ROWTOBOARD(3)][COLTOBOARD(i
)].icon
== 'x')
1541 board
[ROWTOBOARD(3)][COLTOBOARD(i
)].icon
=
1543 if (board
[ROWTOBOARD(6)][COLTOBOARD(i
)].icon
== 'x')
1544 board
[ROWTOBOARD(6)][COLTOBOARD(i
)].icon
=
1549 board
[ROWTOBOARD(crow
)][COLTOBOARD(ccol
)].icon
= c
;
1552 edit_tags(board
, game
[gindex
].tag
, game
[gindex
].tindex
, 0);
1555 if (status
.mode
== MODE_HISTORY
||
1556 status
.engine
== ENGINE_THINKING
)
1559 status
.engine
= ENGINE_THINKING
;
1560 update_status_window();
1561 SEND_TO_ENGINE("go\n");
1564 if (config
.book_method
== -1 || status
.engine
==
1565 ENGINE_THINKING
|| config
.engine
!= GNUCHESS
)
1568 if (config
.book_method
+ 1 >= BOOK_MAX
)
1571 n
= config
.book_method
+ 1;
1573 SEND_TO_ENGINE("book %s\n", book_methods
[n
]);
1576 if (status
.mode
== MODE_HISTORY
) {
1577 if (game
[gindex
].openingside
== BLACK
) {
1578 cmessage(NULL
, ANYKEY
, "%s", E_RESUME_BLACK
);
1582 if (game
[gindex
].hindex
!= game
[gindex
].htotal
) {
1584 if ((c
= message(NULL
, YESNO
, "%s",
1585 GAME_RESUME_HISTORY_TEXT
)) != 'y')
1590 if (TEST_FLAG(game
[gindex
].flags
, GF_GAMEOVER
))
1595 wtimeout(boardw
, 70);
1597 if (!noengine
&& !engine_initialized
) {
1598 if (start_chess_engine() < 0)
1606 oldhistorytotal
= game
[gindex
].htotal
;
1607 game
[gindex
].htotal
= game
[gindex
].hindex
;
1608 status
.mode
= MODE_PLAY
;
1609 status
.engine
= ENGINE_READY
;
1612 if (config
.engine
!= GNUCHESS
)
1613 SEND_TO_ENGINE("read %s\n", config
.fifo
);
1615 SEND_TO_ENGINE("\npgnload %s\n", config
.fifo
);
1621 if (!game
[gindex
].htotal
|| status
.engine
== ENGINE_THINKING
)
1624 wtimeout(boardw
, -1);
1625 init_history(board
);
1628 /* FIXME dies reading FIFO sometimes. */
1629 if (status
.mode
!= MODE_PLAY
|| !game
[gindex
].htotal
)
1632 history_previous(board
, (count
) ? count
* 2 : 2, &crow
, &ccol
);
1633 oldhistorytotal
= game
[gindex
].htotal
;
1634 game
[gindex
].htotal
= game
[gindex
].hindex
;
1636 if (status
.engine
== CRAFTY
)
1637 SEND_TO_ENGINE("read %s\n", config
.fifo
);
1639 SEND_TO_ENGINE("\npgnload %s\n", config
.fifo
);
1641 update_history_window();
1644 if ((tmp
= get_input(GAME_LOAD_TITLE
, NULL
, 1, 1,
1645 BROWSER_PROMPT
, browse_directory
, NULL
,
1649 tmp
= tilde_expand(tmp
);
1651 if (parse_pgn_file(board
, tmp
))
1654 gindex
= gtotal
- 1;
1655 strncpy(loadfile
, tmp
, sizeof(loadfile
));
1656 init_history(board
);
1658 update_tag_window();
1665 n
= message(NULL
, GAME_SAVE_MULTI_PROMPT
, "%s",
1666 GAME_SAVE_MULTI_TEXT
);
1673 update_status_notify("%s", NOTIFY_SAVE_ABORTED
);
1678 if ((tmp
= get_input(GAME_SAVE_TITLE
, loadfile
, 1, 1,
1679 BROWSER_PROMPT
, browse_directory
, NULL
,
1680 '\t', -1)) == NULL
) {
1681 update_status_notify("%s", NOTIFY_SAVE_ABORTED
);
1685 tmp
= tilde_expand(tmp
);
1687 if (strstr(tmp
, ".") == NULL
&& compression_cmd(tmp
, 0)
1689 snprintf(tfile
, sizeof(tfile
), "%s.pgn", tmp
);
1693 if (save_pgn(tmp
, 0, x
)) {
1694 update_status_notify("%s", NOTIFY_SAVE_FAILED
);
1698 update_status_notify("%s", NOTIFY_SAVED
);
1705 n
= help(GAME_HELP_INDEX_TITLE
,
1706 GAME_HELP_INDEX_PROMPT
, mainhelp
);
1710 help(GAME_HELP_HISTORY_TITLE
, ANYKEY
, historyhelp
);
1713 help(GAME_HELP_PLAY_TITLE
, ANYKEY
, playhelp
);
1716 help(GAME_HELP_EDIT_TITLE
, ANYKEY
, edithelp
);
1719 help(GAME_HELP_GAME_TITLE
, ANYKEY
, gamehelp
);
1731 if (cmessage(NULL
, YESNO
, "%s", GAME_NEW_PROMPT
) != 'y')
1735 status
.mode
= MODE_PLAY
;
1746 parse_pgn_file(board
, loadfile
);
1749 game
[gindex
].wcaptures
= game
[gindex
].bcaptures
= 0;
1750 crow
= (status
.side
== WHITE
) ? 2 : 7;
1753 if (!noengine
&& (status
.engine
== ENGINE_OFFLINE
||
1754 engine_initialized
== 0)) {
1755 if (start_chess_engine() < 0)
1759 SEND_TO_ENGINE("\nnew\n");
1760 set_engine_defaults();
1761 status
.engine
= ENGINE_READY
;
1762 update_status_notify(NULL
);
1764 update_tag_window();
1769 keypad(boardw
, TRUE
);
1774 if (status
.engine
== ENGINE_THINKING
)
1777 if (status
.engine
== ENGINE_OFFLINE
)
1780 if ((tmp
= get_input_str_clear(ENGINE_CMD_TITLE
, NULL
))
1782 SEND_TO_ENGINE("%s\n", tmp
);
1786 sp
.icon
= sp
.row
= sp
.col
= 0;
1787 markend
= markstart
= 0;
1791 update_status_notify(NULL
);
1794 if (config
.validmoves
)
1795 reset_valid_moves(board
);
1802 count
= count
* 10 + n
;
1806 update_status_notify("Repeat %i", count
);
1809 if (status
.mode
== MODE_HISTORY
) {
1810 history_next(board
, (count
> 0) ?
1811 config
.jumpcount
* count
* movestep
:
1812 config
.jumpcount
* movestep
, &crow
, &ccol
);
1818 if (sp.icon && config.validmoves) {
1819 get_valid_cursor(board, UP, (count) ? count : 1,
1820 &crow, &ccol, minr, maxr, minc, maxc);
1837 if (status
.mode
== MODE_HISTORY
) {
1838 history_previous(board
, (count
) ?
1839 config
.jumpcount
* count
* movestep
:
1840 config
.jumpcount
* movestep
, &crow
, &ccol
);
1846 if (sp.icon && config.validmoves) {
1847 get_valid_cursor(board, DOWN, (count) ? count : 1,
1848 &crow, &ccol, minr, maxr, minc, maxc);
1856 update_status_notify(NULL
);
1866 if (status
.mode
== MODE_HISTORY
) {
1867 history_previous(board
, (count
) ?
1868 count
* movestep
: movestep
, &crow
, &ccol
);
1874 if (sp.icon && config.validmoves) {
1875 get_valid_cursor(board, LEFT, (count) ? count : 1,
1876 &crow, &ccol, minr, maxr, minc, maxc);
1893 if (status
.mode
== MODE_HISTORY
) {
1894 history_next(board
, (count
) ? count
* movestep
: movestep
,
1901 if (sp.icon && config.validmoves) {
1902 get_valid_cursor(board, RIGHT, (count) ? count : 1,
1903 &crow, &ccol, minr, maxr, minc, maxc);
1920 if (status
.mode
== MODE_HISTORY
)
1923 if (status
.mode
== MODE_EDIT
)
1927 SEND_TO_ENGINE("\nswitch\n");
1929 update_status_window();
1932 if (!editmode
&& status
.mode
== MODE_HISTORY
) {
1938 update_history_window();
1942 if (!noengine
&& (status
.engine
== ENGINE_OFFLINE
||
1943 !engine_initialized
) && !editmode
) {
1944 if (start_chess_engine() < 0) {
1952 wtimeout(boardw
, 70);
1954 if (sp
.icon
|| (!editmode
&& status
.engine
== ENGINE_THINKING
)) {
1959 sp
.icon
= mvwinch(boardw
, ROWTOMATRIX(crow
),
1960 COLTOMATRIX(ccol
)+1) & A_CHARTEXT
;
1962 if (sp
.icon
== ' ') {
1967 if (!editmode
&& ((islower(sp
.icon
) && status
.turn
!= BLACK
) ||
1968 (isupper(sp
.icon
) && status
.turn
!= WHITE
))) {
1969 message(NULL
, ANYKEY
, "%s", E_SELECT_TURN
);
1977 if (!editmode
&& config
.validmoves
) {
1978 get_valid_moves(board
, piece_to_int(sp
.icon
), sp
.row
,
1979 sp
.col
, &minr
, &maxr
, &minc
, &maxc
);
1981 number_valid_moves(board, sp.row, sp.col);
1985 if (status
.mode
== MODE_PLAY
)
1991 pushkey
= count
= 0;
1992 update_status_notify(NULL
);
1994 if (!editmode
&& status
.mode
== MODE_HISTORY
)
1997 if (status
.engine
== ENGINE_THINKING
) {
2010 sp
.icon
= sp
.row
= sp
.col
= 0;
2014 if (move_to_engine(board
)) {
2015 if (config
.validmoves
)
2016 reset_valid_moves(board
);
2018 if (TEST_FLAG(game
[gindex
].flags
, GF_GAMEOVER
)) {
2019 CLEAR_FLAG(game
[gindex
].flags
, GF_GAMEOVER
);
2020 SET_FLAG(game
[gindex
].flags
, GF_MODIFIED
);
2041 void usage(const char *pn
, int ret
)
2045 for (i
= 0; cmdlinehelp
[i
]; i
++)
2046 fputs(cmdlinehelp
[i
], stderr
);
2051 void catch_signal(int which
)
2063 cmessage(NULL
, ANYKEY
, "%s", E_BROKEN_PIPE
);
2072 keypad(boardw
, TRUE
);
2081 int main(int argc
, char *argv
[])
2085 char buf
[FILENAME_MAX
];
2086 char datadir
[FILENAME_MAX
];
2087 int ret
= EXIT_SUCCESS
;
2088 int validate
= 0, validate_and_save
= 0;
2090 if ((config
.pwd
= getpwuid(getuid())) == NULL
)
2091 err(EXIT_FAILURE
, "getpwuid()");
2093 snprintf(datadir
, sizeof(datadir
), "%s/.cboard", config
.pwd
->pw_dir
);
2094 snprintf(buf
, sizeof(buf
), "%s/cc.data", datadir
);
2095 config
.ccfile
= strdup(buf
);
2096 snprintf(buf
, sizeof(buf
), "%s/nag.data", datadir
);
2097 config
.nagfile
= strdup(buf
);
2098 snprintf(buf
, sizeof(buf
), "%s/agony.data", datadir
);
2099 config
.agonyfile
= strdup(buf
);
2100 snprintf(buf
, sizeof(buf
), "%s/config", datadir
);
2101 config
.configfile
= strdup(buf
);
2102 snprintf(buf
, sizeof(buf
), "%s/fifo", datadir
);
2103 config
.fifo
= strdup(buf
);
2104 snprintf(buf
, sizeof(buf
), "%s/tmpfile", datadir
);
2105 config
.tmpfile
= strdup(buf
);
2107 if (stat(datadir
, &st
) == -1) {
2108 if (errno
== ENOENT
) {
2109 if (mkdir(datadir
, 0755) == -1)
2110 err(EXIT_FAILURE
, "%s", datadir
);
2113 err(EXIT_FAILURE
, "%s", datadir
);
2118 if (!S_ISDIR(st
.st_mode
))
2119 errx(EXIT_FAILURE
, "%s: %s", datadir
, E_NOTADIR
);
2121 if (access(config
.fifo
, R_OK
) == -1 && errno
== ENOENT
) {
2122 if (mkfifo(config
.fifo
, 0600) == -1)
2123 err(EXIT_FAILURE
, "%s", config
.fifo
);
2129 while ((opt
= getopt(argc
, argv
, "EDNVShp:vu:e:f:i:")) != -1) {
2131 while ((opt
= getopt(argc
, argv
, "ENVShp:vu:e:f:i:")) != -1) {
2138 config
.stoponerror
= 1;
2144 validate_and_save
= 1;
2156 while ((tmp
= strsep(&optarg
, ":")) != NULL
) {
2159 config
.ics_user
= optarg
;
2162 config
.ics_passwd
= optarg
;
2165 usage(argv
[0], EXIT_FAILURE
);
2172 while ((tmp
= strsep(&optarg
, ":")) != NULL
) {
2175 strncpy(config
.ics_server
, tmp
,
2176 sizeof(config
.ics_server
));
2179 if (!isinteger(tmp
))
2180 usage(argv
[0], EXIT_FAILURE
);
2182 config
.ics_port
= atoi(tmp
);
2185 usage(argv
[0], EXIT_FAILURE
);
2190 printf("%s (%s)\n%s\n", PACKAGE_STRING
, curses_version(),
2194 filetype
= PGN_FILE
;
2195 strncpy(loadfile
, optarg
, sizeof(loadfile
));
2198 filetype
= FEN_FILE
;
2199 strncpy(loadfile
, optarg
, sizeof(loadfile
));
2202 filetype
= EPD_FILE
;
2203 strncpy(loadfile
, optarg
, sizeof(loadfile
));
2207 usage(argv
[0], EXIT_SUCCESS
);
2211 if ((validate
|| validate_and_save
) && !*loadfile
)
2212 usage(argv
[0], EXIT_FAILURE
);
2214 if (access(config
.configfile
, R_OK
) == 0)
2215 parse_rcfile(config
.configfile
);
2217 signal(SIGPIPE
, catch_signal
);
2218 signal(SIGCONT
, catch_signal
);
2219 signal(SIGSTOP
, catch_signal
);
2220 signal(SIGINT
, catch_signal
);
2226 ret
= parse_pgn_file(board
, loadfile
);
2229 ret
= parse_fen_file(board
, loadfile
);
2236 if (validate
|| validate_and_save
) {
2237 if (validate_and_save
) {
2240 for (i
= 0; i
< gtotal
; i
++)
2241 pgn_dumpgame(stdout
, &game
[i
], i
, 0);
2247 if (initscr() == NULL
)
2248 errx(EXIT_FAILURE
, "%s", E_INITCURSES
);
2250 curses_initialized
= 1;
2252 if (has_colors() == TRUE
&& start_color() == OK
)
2255 boardw
= newwin(BOARD_HEIGHT
, BOARD_WIDTH
, 0, COLS
- BOARD_WIDTH
);
2256 boardp
= new_panel(boardw
);
2257 historyw
= newwin(HISTORY_HEIGHT
, HISTORY_WIDTH
, LINES
- HISTORY_HEIGHT
,
2258 COLS
- HISTORY_WIDTH
);
2259 historyp
= new_panel(historyw
);
2260 statusw
= newwin(STATUS_HEIGHT
, STATUS_WIDTH
, LINES
- STATUS_HEIGHT
, 0);
2261 statusp
= new_panel(statusw
);
2262 tagw
= newwin(TAG_HEIGHT
, TAG_WIDTH
, 0, 0);
2263 tagp
= new_panel(tagw
);
2264 keypad(boardw
, TRUE
);
2265 // leaveok(boardw, TRUE);
2266 leaveok(tagw
, TRUE
);
2267 leaveok(statusw
, TRUE
);
2268 leaveok(historyw
, TRUE
);
2273 wbkgd(boardw
, CP_BOARD_WINDOW
);
2274 wbkgd(statusw
, CP_STATUS_WINDOW
);
2275 draw_window_title(statusw
, STATUS_WINDOW_TITLE
, STATUS_WIDTH
,
2276 CP_STATUS_TITLE
, CP_STATUS_BORDER
);
2277 wbkgd(tagw
, CP_TAG_WINDOW
);
2278 draw_window_title(tagw
, TAG_WINDOW_TITLE
, TAG_WIDTH
, CP_TAG_TITLE
,
2280 wbkgd(historyw
, CP_HISTORY_WINDOW
);
2281 draw_window_title(historyw
, HISTORY_WINDOW_TITLE
, HISTORY_WIDTH
,
2282 CP_HISTORY_TITLE
, CP_HISTORY_BORDER
);
2291 del_panel(historyp
);