From bbdae69de4079c36350b0798e92937529c70dd48 Mon Sep 17 00:00:00 2001 From: John Zaitseff Date: Mon, 8 Aug 2011 20:47:52 +1000 Subject: [PATCH] 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. --- .gitignore | 2 +- lib/.gitignore | 21 +++++++++++------- m4/.gitignore | 15 +++++++------ m4/gnulib-cache.m4 | 4 +++- src/exch.c | 15 +++++++++++++ src/game.c | 42 ++++++++++++++++++++++++++++++------ src/globals.c | 3 ++- src/globals.h | 4 +++- src/help.c | 6 ++++++ src/intf.c | 53 +++++++++++++++++++++++++++++++++++++++++++--- src/intf.h | 11 +++++----- src/move.c | 52 ++++++++++++++++++++++++++++++++++++++++++--- src/system.h | 1 + src/trader.c | 2 +- 14 files changed, 195 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 2f1337e..d10ac52 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/lib/.gitignore b/lib/.gitignore index 723eb71..40cf3ec 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -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 diff --git a/m4/.gitignore b/m4/.gitignore index 76961e1..d9056c6 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -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 diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4 index da62d61..94ec404 100644 --- a/m4/gnulib-cache.m4 +++ b/m4/gnulib-cache.m4 @@ -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 diff --git a/src/exch.c b/src/exch.c index 9535ff5..d426608 100644 --- a/src/exch.c +++ b/src/exch.c @@ -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(); } } diff --git a/src/game.c b/src/game.c index 3e13573..18a6837 100644 --- a/src/game.c +++ b/src/game.c @@ -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, diff --git a/src/globals.c b/src/globals.c index 3deab67..af8e74e 100644 --- a/src/globals.c +++ b/src/globals.c @@ -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 diff --git a/src/globals.h b/src/globals.h index db19be3..135a717 100644 --- a/src/globals.h +++ b/src/globals.h @@ -33,6 +33,7 @@ #include +#include /************************************************************************ @@ -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 */ diff --git a/src/help.c b/src/help.c index 4f9a520..938848c 100644 --- a/src/help.c +++ b/src/help.c @@ -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); } diff --git a/src/intf.c b/src/intf.c index 6214570..0ef038b 100644 --- a/src/intf.c +++ b/src/intf.c @@ -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(); } diff --git a/src/intf.h b/src/intf.h index 6301c2a..2887925 100644 --- a/src/intf.h +++ b/src/intf.h @@ -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); /* diff --git a/src/move.c b/src/move.c index 3db159b..8d41845 100644 --- a/src/move.c +++ b/src/move.c @@ -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; diff --git a/src/system.h b/src/system.h index 6b03183..1e6fb58 100644 --- a/src/system.h +++ b/src/system.h @@ -64,6 +64,7 @@ // Headers defined by X/Open Single Unix Specification v4 #include +#include #include #include #include diff --git a/src/trader.c b/src/trader.c index b47dda6..8ccc6e8 100644 --- a/src/trader.c +++ b/src/trader.c @@ -180,7 +180,7 @@ int main (int argc, char *argv[]) // Finish up... end_program(); - return EXIT_SUCCESS; + return abort_game ? EXIT_FAILURE : EXIT_SUCCESS; }