From 26f0ba3074047d074e71c904baccfce0763e346d Mon Sep 17 00:00:00 2001 From: cos Date: Wed, 16 Sep 2015 22:37:59 +0200 Subject: Prompt on interactive quit when confirmquit is true. Quitting a window manager is a very special and dangerous operation compared to exiting an editor, a web browser or something else with a command syntax very similar to ratpoison's. This commit adds some protection for those (us) who occasionally unintentionally types quit at the wrong prompt. --- doc/ratpoison.mdoc.1 | 7 ++++++ doc/ratpoison.texi | 1 + src/actions.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/data.h | 3 +++ src/main.c | 2 ++ 5 files changed, 81 insertions(+), 2 deletions(-) diff --git a/doc/ratpoison.mdoc.1 b/doc/ratpoison.mdoc.1 index 2e3c356..941f503 100644 --- a/doc/ratpoison.mdoc.1 +++ b/doc/ratpoison.mdoc.1 @@ -1141,6 +1141,13 @@ Decide if history expansion using ! is available. Default is .Li 0 (off). +.It Cm confirmquit Li 0 | 1 +Protect against unintended window manager termination by prompting for +confirmation when calling quit. (Only in interactive mode.) +.Pp +Default is +.Li 0 +(off). .El .Sh FILES .Bl -tag -width "%%sysconfdir%%/ratpoisonrc" -compact diff --git a/doc/ratpoison.texi b/doc/ratpoison.texi index da9da16..fb8e91d 100644 --- a/doc/ratpoison.texi +++ b/doc/ratpoison.texi @@ -1672,6 +1672,7 @@ Here is a list of variables that can be set: @item historysize @item historycompaction @item historyexpansion +@item confirmquit @end itemize @end deffn diff --git a/src/actions.c b/src/actions.c index c0fc569..4bfe639 100644 --- a/src/actions.c +++ b/src/actions.c @@ -83,6 +83,7 @@ static cmdret * set_historycompaction (struct cmdarg **args); static cmdret * set_historyexpansion (struct cmdarg **args); static cmdret * set_msgwait(struct cmdarg **args); static cmdret * set_framemsgwait(struct cmdarg **args); +static cmdret * set_confirmquit(struct cmdarg **args); LIST_HEAD(set_vars); @@ -154,6 +155,7 @@ init_set_vars(void) add_set_var ("historyexpansion", set_historyexpansion, 1, "", arg_NUMBER); add_set_var ("msgwait", set_msgwait, 1, "", arg_NUMBER); add_set_var ("framemsgwait", set_framemsgwait, 1, "", arg_NUMBER); + add_set_var ("confirmquit", set_confirmquit, 1, "", arg_NUMBER); } /* rp_keymaps is ratpoison's list of keymaps. */ @@ -188,6 +190,41 @@ add_command (char *name, cmdret * (*fn)(int, struct cmdarg **), int nargs, int i list_add (&cmd->node, &user_commands); } +static void +modify_command (char *name, cmdret * (*fn)(int, struct cmdarg **), int nargs, int i_nrequired, int ni_nrequired, ...) +{ + int i = 0; + struct user_command *cmd; + va_list va; + struct user_command *cur; + + cmd = xmalloc (sizeof (struct user_command)); + cmd->name = name; + cmd->func = fn; + cmd->num_args = nargs; + cmd->ni_required_args = ni_nrequired; + cmd->i_required_args = i_nrequired; + cmd->args = nargs ? xmalloc (nargs * sizeof (struct argspec)) : NULL; + + /* Remove existing command */ + list_for_each_entry (cur, &user_commands, node) + { + if(&cur->func == &cmd->func) + list_del_init(&(cur->node)); + } + + /* Fill cmd->args */ + va_start(va, ni_nrequired); + for (i=0; iargs[i].prompt = va_arg(va, char*); + cmd->args[i].type = va_arg(va, int); + } + va_end(va); + + /* Add updated command */ + list_add (&cmd->node, &user_commands); +} static void user_command_free(struct user_command *cmd) @@ -314,7 +351,8 @@ init_user_commands(void) add_command ("other", cmd_other, 0, 0, 0); add_command ("prev", cmd_prev, 0, 0, 0); add_command ("prevscreen", cmd_prevscreen, 0, 0, 0); - add_command ("quit", cmd_quit, 0, 0, 0); + add_command ("quit", cmd_quit, 1, 0, 0, + "Really quit? (yes/no): ", arg_STRING); add_command ("ratinfo", cmd_ratinfo, 0, 0, 0); add_command ("ratrelinfo", cmd_ratrelinfo, 0, 0, 0); add_command ("banishrel", cmd_banishrel, 0, 0, 0); @@ -2747,7 +2785,18 @@ cmd_newwm(int interactive UNUSED, struct cmdarg **args) cmdret * cmd_quit(int interactive UNUSED, struct cmdarg **args UNUSED) { - kill_signalled = 1; + const char *confirm = (args[0] ? ARG_STRING(0) : NULL); + + if(defaults.confirm_on_quit) { + if(str_comp((char *) confirm, "yes", 3) && (strlen(confirm)==3)) { + kill_signalled = 1; + } else { + return cmdret_new (RET_FAILURE, "Aborting quit command."); + } + } else { + kill_signalled = 1; + } + return cmdret_new (RET_SUCCESS, NULL); } @@ -4237,6 +4286,23 @@ set_bwcolor (struct cmdarg **args) return cmdret_new (RET_SUCCESS, NULL); } +static cmdret * +set_confirmquit (struct cmdarg **args) +{ + if (args[0] == NULL) + return cmdret_new (RET_SUCCESS, "%d", defaults.confirm_on_quit); + + if (ARG(0,number) < 0) + return cmdret_new (RET_FAILURE, "confirm_on_quit: %s", invalid_negative_arg); + else + defaults.confirm_on_quit = ARG(0,number); + + modify_command ("quit", cmd_quit, 1, (defaults.confirm_on_quit ? 1 : 0), 0, + "Really quit? (yes/no): ", arg_STRING); + + return cmdret_new (RET_SUCCESS, NULL); +} + cmdret * cmd_setenv (int interactive UNUSED, struct cmdarg **args) { diff --git a/src/data.h b/src/data.h index f4bd185..4ccecbf 100644 --- a/src/data.h +++ b/src/data.h @@ -275,6 +275,9 @@ struct rp_defaults /* Frame indicator format */ char *frame_fmt; + + /* Prompt on quit */ + int confirm_on_quit; }; /* Information about a child process. */ diff --git a/src/main.c b/src/main.c index 52dbcce..da6a0c0 100644 --- a/src/main.c +++ b/src/main.c @@ -599,6 +599,8 @@ init_defaults (void) defaults.history_expansion = False; defaults.frame_selectors = xstrdup (""); defaults.maxundos = 20; + + defaults.confirm_on_quit = 0; } int -- cgit v1.2.3