1
0
mirror of https://git.zap.org.au/git/trader.git synced 2024-06-09 19:20:43 +00:00

Add a signal handler for SIGINT and SIGTERM

Add a signal handler for SIGINT and SIGTERM, as well as sprinklings of
checks to abort_game.  Although Ncurses DOES define handlers for SIGINT
and SIGTERM, they do not always seem to work correctly under many
operating systems.
This commit is contained in:
John Zaitseff 2011-08-08 20:47:52 +10:00
parent 91eb4d65d8
commit bbdae69de4
14 changed files with 195 additions and 36 deletions

2
.gitignore vendored
View File

@ -21,11 +21,11 @@ Makefile.in
/build-aux/install-sh
/build-aux/missing
/build-aux/snippet/_Noreturn.h
/build-aux/snippet/arg-nonnull.h
/build-aux/snippet/c++defs.h
/build-aux/snippet/unused-parameter.h
/build-aux/snippet/warn-on-use.h
/build-aux/snippet/_Noreturn.h
/po/POTFILES
/po/remove-potcdate.sed

21
lib/.gitignore vendored
View File

@ -1,17 +1,17 @@
Makefile.am
alloca.in.h
asnprintf.c
ctype.in.h
c-ctype.c
c-ctype.h
c-strcase.h
c-strcasecmp.c
c-strncasecmp.c
ctype.in.h
dosname.h
errno.in.h
float+.h
float.c
float.in.h
float+.h
fprintf.c
fpucw.h
frexp.c
@ -34,19 +34,18 @@ iconv_open-osf.gperf
iconv_open-solaris.gperf
iconv_open.c
isnan.c
isnand.c
isnand-nolibm.h
isnanf.c
isnand.c
isnanf-nolibm.h
isnanl.c
isnanf.c
isnanl-nolibm.h
isnanl.c
langinfo.in.h
locale.in.h
malloc.c
math.in.h
memchr.c
memchr.valgrind
printf.c
printf-args.c
printf-args.h
printf-frexp.c
@ -55,9 +54,14 @@ printf-frexpl.c
printf-frexpl.h
printf-parse.c
printf-parse.h
printf.c
sig-handler.h
sigaction.c
signal.in.h
signbitd.c
signbitf.c
signbitl.c
sigprocmask.c
size_max.h
snprintf.c
stat.c
@ -65,16 +69,16 @@ stdarg.in.h
stdbool.in.h
stddef.in.h
stdint.in.h
stdio.in.h
stdio-impl.h
stdio.in.h
stdlib.in.h
str-two-way.h
strdup.c
striconv.c
striconv.h
string.in.h
strncat.c
strstr.c
str-two-way.h
sys_stat.in.h
sys_time.in.h
time.in.h
@ -103,6 +107,7 @@ iconv_open-solaris.h
langinfo.h
locale.h
math.h
signal.h
stdio.h
stdlib.h
string.h

15
m4/.gitignore vendored
View File

@ -19,8 +19,8 @@ frexpl.m4
getopt.m4
gettext.m4
gettimeofday.m4
glibc21.m4
glibc2.m4
glibc21.m4
gnulib-common.m4
gnulib-comp.m4
gnulib-tool.m4
@ -30,8 +30,8 @@ iconv_open.m4
include_next.m4
inline.m4
intdiv0.m4
intldir.m4
intl.m4
intldir.m4
intlmacosx.m4
intmax.m4
intmax_t.m4
@ -59,16 +59,19 @@ multiarch.m4
nls.m4
nocrash.m4
po.m4
printf.m4
printf-frexp.m4
printf-frexpl.m4
printf-posix.m4
printf-posix-rpl.m4
printf-posix.m4
printf.m4
progtest.m4
sigaction.m4
signal_h.m4
signalblocking.m4
signbit.m4
size_max.m4
snprintf.m4
snprintf-posix.m4
snprintf.m4
stat.m4
stdarg.m4
stdbool.m4
@ -90,8 +93,8 @@ unistd_h.m4
vasnprintf.m4
vfprintf-posix.m4
visibility.m4
vsnprintf.m4
vsnprintf-posix.m4
vsnprintf.m4
warn-on-use.m4
wchar_h.m4
wchar_t.m4

View File

@ -15,7 +15,7 @@
# Specification in the form of a command-line invocation:
# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --no-conditional-dependencies --no-libtool --macro-prefix=gl assert config-h ctype fprintf-posix getopt-gnu gettext gettext-h gettimeofday langinfo locale printf-posix snprintf-posix stat stdarg stdbool stdio strdup-posix striconv string strncat strstr sys_stat sys_time unistd vfprintf-posix vsnprintf-posix
# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --no-conditional-dependencies --no-libtool --macro-prefix=gl assert config-h ctype fprintf-posix getopt-gnu gettext gettext-h gettimeofday langinfo locale printf-posix sigaction signal snprintf-posix stat stdarg stdbool stdio strdup-posix striconv string strncat strstr sys_stat sys_time unistd vfprintf-posix vsnprintf-posix
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([])
@ -31,6 +31,8 @@ gl_MODULES([
langinfo
locale
printf-posix
sigaction
signal
snprintf-posix
stat
stdarg

View File

@ -211,6 +211,9 @@ void exchange_stock (void)
break;
default:
if (abort_game)
return;
beep();
}
}
@ -256,6 +259,9 @@ void visit_bank (void)
char *buf;
if (abort_game)
return;
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
@ -328,6 +334,9 @@ void visit_bank (void)
break;
default:
if (abort_game)
return;
beep();
}
}
@ -482,6 +491,9 @@ void trade_shares (int num, bool *bid_used)
char *buf;
if (abort_game)
return;
assert(num >= 0 && num < MAX_COMPANIES);
assert(company[num].on_map);
@ -581,6 +593,9 @@ void trade_shares (int num, bool *bid_used)
break;
default:
if (abort_game)
return;
beep();
}
}

View File

@ -126,6 +126,9 @@ void init_game (void)
while (number_players == 0) {
int choice = ask_number_players();
if (abort_game)
return;
if (choice == ERR) {
abort_game = true;
return;
@ -133,6 +136,9 @@ void init_game (void)
} else if (choice == 0) {
choice = ask_game_number();
if (abort_game)
return;
if (choice != ERR) {
game_num = choice;
@ -162,6 +168,9 @@ void init_game (void)
ask_player_names();
if (abort_game)
return;
deltxwin(); // "Number of players" window
txrefresh();
@ -221,7 +230,6 @@ void init_game (void)
}
quit_selected = false;
abort_game = false;
}
@ -277,6 +285,9 @@ static int ask_number_players (void)
break;
default:
if (abort_game)
return ERR;
beep();
}
}
@ -333,6 +344,9 @@ int ask_game_number (void)
break;
default:
if (abort_game)
return ERR;
beep();
}
}
@ -348,6 +362,9 @@ int ask_game_number (void)
void ask_player_names (void)
{
if (abort_game)
return;
if (number_players == 1) {
// Ask for the player's name
@ -362,6 +379,9 @@ void ask_player_names (void)
while (true) {
int ret = gettxstr(curwin, &player[0].name, NULL, false,
2, x, w, attr_input_field);
if (abort_game)
return;
if (ret == OK && strlen(player[0].name) != 0) {
break;
} else {
@ -371,7 +391,7 @@ void ask_player_names (void)
newtxwin(5, 44, 6, WCENTER, true, attr_normal_window);
mvwaddstr(curwin, 2, 2, "Do you need any instructions?");
if (answer_yesno(curwin, attr_keycode)) {
if (answer_yesno(curwin, attr_keycode) == true) {
show_help();
}
@ -432,6 +452,9 @@ void ask_player_names (void)
break;
case ERR:
if (abort_game)
return;
beep();
break;
@ -468,7 +491,7 @@ void ask_player_names (void)
newtxwin(5, 50, 6, WCENTER, true, attr_normal_window);
mvwaddstr(curwin, 2, 2, "Does any player need instructions?");
if (answer_yesno(curwin, attr_keycode)) {
if (answer_yesno(curwin, attr_keycode) == true) {
show_help();
}
}
@ -487,10 +510,8 @@ void end_game (void)
char *buf;
if (abort_game) {
// init_game() was cancelled by user
if (abort_game)
return;
}
buf = malloc(BUFSIZE);
if (buf == NULL) {
@ -510,6 +531,9 @@ void end_game (void)
show_status(i);
}
if (abort_game)
return;
if (number_players == 1) {
l_strfmon(buf, BUFSIZE, "%1n", total_value(0));
@ -571,6 +595,9 @@ void show_map (bool closewin)
int n, x, y;
if (abort_game)
return;
newtxwin(MAX_Y + 4, WIN_COLS, 1, WCENTER, true, attr_map_window);
// Draw various borders
@ -670,6 +697,9 @@ void show_status (int num)
int i, line;
if (abort_game)
return;
assert(num >= 0 && num < number_players);
newtxwin(MAX_COMPANIES + 15, WIN_COLS, 1, WCENTER, true,

View File

@ -84,12 +84,13 @@ bool game_loaded = false; // True if game was loaded from disk
int game_num = 0; // Game number (1-9)
bool quit_selected = false; // Is a player trying to quit the game?
bool abort_game = false; // Abort game without declaring winner?
bool option_no_color = false; // True if --no-color was specified
bool option_dont_encrypt = false; // True if --dont-encrypt was specified
int option_max_turn = 0; // Max. turns if --max-turn was specified
volatile sig_atomic_t abort_game = false; // Abort game as quickly as possible?
/***********************************************************************/
// End of file

View File

@ -33,6 +33,7 @@
#include <stdbool.h>
#include <signal.h>
/************************************************************************
@ -195,11 +196,12 @@ extern bool game_loaded; // True if game was loaded from disk
extern int game_num; // Game number (1-9)
extern bool quit_selected; // Is a player trying to quit the game?
extern bool abort_game; // Abort game without declaring winner?
extern bool option_no_color; // True if --no-color was specified
extern bool option_dont_encrypt; // True if --dont-encrypt was specified
extern int option_max_turn; // Max. turns if --max-turn was specified
extern volatile sig_atomic_t abort_game; // Abort game as quickly as possible?
#endif /* included_GLOBALS_H */

View File

@ -162,6 +162,9 @@ void show_help (void)
bool done = false;
if (abort_game)
return;
// Count how many pages appear in the help text
for (numpages = 0; help_text[numpages] != NULL; numpages++)
;
@ -356,6 +359,9 @@ void show_help (void)
break;
default:
if (abort_game)
return;
curpage++;
done = (curpage == numpages);
}

View File

@ -101,6 +101,7 @@ txwin_t *firstwin = NULL; // First (bottom-most) txwin structure
Parameters: dest - Destination buffer of size BUFSIZE
src - Source buffer of size BUFSIZE
isfloat - True if src contains a floating point number
Returns: (nothing)
This helper function copies the string in src to dest, performing
certain fixups along the way. In particular, thousands separators are
@ -114,6 +115,17 @@ static void txinput_fixup (char *restrict dest, char *restrict src,
bool isfloat);
/*
Function: sigterm_handler - Handle program termination signals
Parameters: sig - Signal number
Returns: (nothing)
This function handles termination signals (like SIGINT and SIGTERM) by
setting the global variable abort_game to true.
*/
static void sigterm_handler (int sig);
/************************************************************************
* Basic text input/output function definitions *
************************************************************************/
@ -126,6 +138,22 @@ static void txinput_fixup (char *restrict dest, char *restrict src,
void init_screen (void)
{
struct sigaction sa;
// Initialise signal handlers
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sigterm_handler;
if (sigaction(SIGINT, &sa, NULL) == -1) {
errno_exit("sigaction(SIGINT)");
}
if (sigaction(SIGTERM, &sa, NULL) == -1) {
errno_exit("sigaction(SIGTERM)");
}
// Initialise the screen
initscr();
if (COLS < MIN_COLS || LINES < MIN_LINES) {
@ -259,6 +287,21 @@ void end_screen (void)
}
/***********************************************************************/
// sigterm_handler: Handle program termination signals
void sigterm_handler (int sig)
{
/* Note that sigterm_handler() simply sets a volatile variable
instead of directly terminating, as functions like endwin() and
end_screen() are NOT reentrant. A side-effect of doing this,
however, is that the main program needs to have abort_game checks
scattered all over it. */
abort_game = true;
}
/***********************************************************************/
// newtxwin: Create a new window, inserted into window stack
@ -642,8 +685,10 @@ int gettxline (WINDOW *win, char *buf, int bufsize, bool *restrict modified,
key = wgetch(win);
if (key == ERR) {
// Do nothing on ERR
;
if (abort_game) {
ret = ERR;
done = true;
}
} else if ((key == KEY_DEFAULTVAL1 || key == KEY_DEFAULTVAL2)
&& defaultval != NULL && len == 0) {
// Initialise buffer with the default value
@ -1447,7 +1492,7 @@ int gettxlong (WINDOW *win, long int *restrict result, long int min,
/***********************************************************************/
// answer_yesno: Wait for a Yes/No answer
bool answer_yesno (WINDOW *win, chtype attr_keys)
int answer_yesno (WINDOW *win, chtype attr_keys)
{
int key;
bool done;
@ -1474,6 +1519,8 @@ bool answer_yesno (WINDOW *win, chtype attr_keys)
if (key == 'Y' || key == 'N') {
done = true;
} else if (abort_game) {
return ERR;
} else {
beep();
}

View File

@ -534,16 +534,17 @@ extern int gettxlong (WINDOW *win, long int *restrict result, long int min,
Function: answer_yesno - Wait for a Yes/No answer
Parameters: win - Window to use (should be curwin)
attr_keys - Window rendition to use for key choices
Returns: bool - True if Yes was selected, false if No
Returns: int - 1 if Yes was selected, 0 if No, ERR if error
This function prompts the user by printing " [Y/N] " using appropriate
character renditions ("Y" and "N" in attr_keys, the rest in the current
rendition), then waits for the user to press either "Y" (for Yes) or
"N" (for No) on the keyboard, then prints the answer using A_BOLD.
True is returned if "Y" was selected, false if "N". Note that the
cursor becomes invisible after calling this function.
"N" (for No) on the keyboard, then prints the answer using A_BOLD. If
"Y" was selected, 1 is returned, if "N", 0 is returned. ERR is
returned if abort_game becomes true. Note that the cursor becomes
invisible after calling this function.
*/
extern bool answer_yesno (WINDOW *win, chtype attr_keys);
extern int answer_yesno (WINDOW *win, chtype attr_keys);
/*

View File

@ -159,6 +159,9 @@ void select_moves (void)
bool unique;
if (abort_game)
return;
// How many empty spaces are there in the galaxy map?
count = 0;
for (x = 0; x < MAX_X; x++) {
@ -318,6 +321,9 @@ selection_t get_move (void)
break;
default:
if (abort_game)
return SEL_QUIT;
beep();
}
}
@ -335,10 +341,13 @@ selection_t get_move (void)
// Ask the player to confirm their choice
mvwaddstr(curwin, 2, 22, "Are you sure?");
if (! answer_yesno(curwin, attr_keycode)) {
if (answer_yesno(curwin, attr_keycode) != true) {
selection = SEL_NONE;
}
if (abort_game)
return SEL_QUIT;
// Save the game if required
if (selection == SEL_SAVE) {
bool saved = false;
@ -397,6 +406,9 @@ selection_t get_move (void)
break;
default:
if (abort_game)
return SEL_QUIT;
beep();
}
}
@ -444,12 +456,13 @@ selection_t get_move (void)
void process_move (selection_t selection)
{
if (abort_game)
return;
if (selection == SEL_QUIT) {
// The players want to end the game
quit_selected = true;
}
if (quit_selected || abort_game) {
deltxwin(); // "Select move" window
deltxwin(); // Galaxy map window
txrefresh();
@ -495,6 +508,9 @@ void process_move (selection_t selection)
assign_vals(x, y, left, right, up, down);
}
if (abort_game)
return;
if (IS_MAP_COMPANY(left) && IS_MAP_COMPANY(up)
&& left != up) {
galaxy_map[x][y] = left;
@ -502,6 +518,9 @@ void process_move (selection_t selection)
assign_vals(x, y, left, right, up, down);
}
if (abort_game)
return;
if (IS_MAP_COMPANY(left) && IS_MAP_COMPANY(down)
&& left != down) {
galaxy_map[x][y] = left;
@ -509,6 +528,9 @@ void process_move (selection_t selection)
assign_vals(x, y, left, right, up, down);
}
if (abort_game)
return;
if (IS_MAP_COMPANY(right) && IS_MAP_COMPANY(up)
&& right != up) {
galaxy_map[x][y] = right;
@ -516,6 +538,9 @@ void process_move (selection_t selection)
assign_vals(x, y, left, right, up, down);
}
if (abort_game)
return;
if (IS_MAP_COMPANY(right) && IS_MAP_COMPANY(down)
&& right != down) {
galaxy_map[x][y] = right;
@ -523,6 +548,9 @@ void process_move (selection_t selection)
assign_vals(x, y, left, right, up, down);
}
if (abort_game)
return;
if (IS_MAP_COMPANY(up) && IS_MAP_COMPANY(down)
&& up != down) {
galaxy_map[x][y] = up;
@ -531,6 +559,9 @@ void process_move (selection_t selection)
}
}
if (abort_game)
return;
// See if an existing company can be expanded
nearby = (IS_MAP_COMPANY(left) ? left :
(IS_MAP_COMPANY(right) ? right :
@ -596,6 +627,9 @@ void next_player (void)
bool all_out;
if (abort_game)
return;
all_out = true;
for (i = 0; i < number_players; i++) {
if (player[i].in_game) {
@ -636,6 +670,9 @@ void bankrupt_player (bool forced)
int i;
if (abort_game)
return;
/* It would be nice if we had functions that would do word-wrapping
for us automatically! */
@ -790,6 +827,9 @@ void merge_companies (map_val_t a, map_val_t b)
char *buf;
if (abort_game)
return;
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
@ -952,6 +992,9 @@ void adjust_values (void)
int which;
if (abort_game)
return;
// Declare a company bankrupt!
if (randf() > (1.0 - COMPANY_BANKRUPTCY)) {
which = randi(MAX_COMPANIES);
@ -1085,6 +1128,9 @@ void adjust_values (void)
interest_rate /= randf() + INTEREST_RATE_DIVIDER;
}
if (abort_game)
return;
// Calculate current player's debt
player[current_player].debt *= interest_rate + 1.0;

View File

@ -64,6 +64,7 @@
// Headers defined by X/Open Single Unix Specification v4
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <monetary.h>

View File

@ -180,7 +180,7 @@ int main (int argc, char *argv[])
// Finish up...
end_program();
return EXIT_SUCCESS;
return abort_game ? EXIT_FAILURE : EXIT_SUCCESS;
}