Coverage Report

Created: 2025-07-12 06:33

/src/tmux/cmd-confirm-before.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD$ */
2
3
/*
4
 * Copyright (c) 2009 Tiago Cunha <me@tiagocunha.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <ctype.h>
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include "tmux.h"
26
27
/*
28
 * Asks for confirmation before executing a command.
29
 */
30
31
static enum args_parse_type cmd_confirm_before_args_parse(struct args *,
32
            u_int, char **);
33
static enum cmd_retval    cmd_confirm_before_exec(struct cmd *,
34
            struct cmdq_item *);
35
36
static int  cmd_confirm_before_callback(struct client *, void *,
37
        const char *, int);
38
static void cmd_confirm_before_free(void *);
39
40
const struct cmd_entry cmd_confirm_before_entry = {
41
  .name = "confirm-before",
42
  .alias = "confirm",
43
44
  .args = { "bc:p:t:y", 1, 1, cmd_confirm_before_args_parse },
45
  .usage = "[-by] [-c confirm-key] [-p prompt] " CMD_TARGET_CLIENT_USAGE
46
     " command",
47
48
  .flags = CMD_CLIENT_TFLAG,
49
  .exec = cmd_confirm_before_exec
50
};
51
52
struct cmd_confirm_before_data {
53
  struct cmdq_item  *item;
54
  struct cmd_list   *cmdlist;
55
  u_char       confirm_key;
56
  int      default_yes;
57
};
58
59
static enum args_parse_type
60
cmd_confirm_before_args_parse(__unused struct args *args, __unused u_int idx,
61
    __unused char **cause)
62
0
{
63
0
  return (ARGS_PARSE_COMMANDS_OR_STRING);
64
0
}
65
66
static enum cmd_retval
67
cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
68
0
{
69
0
  struct args     *args = cmd_get_args(self);
70
0
  struct cmd_confirm_before_data  *cdata;
71
0
  struct client     *tc = cmdq_get_target_client(item);
72
0
  struct cmd_find_state   *target = cmdq_get_target(item);
73
0
  char        *new_prompt;
74
0
  const char      *confirm_key, *prompt, *cmd;
75
0
  int        wait = !args_has(args, 'b');
76
77
0
  cdata = xcalloc(1, sizeof *cdata);
78
0
  cdata->cmdlist = args_make_commands_now(self, item, 0, 1);
79
0
  if (cdata->cmdlist == NULL) {
80
0
    free(cdata);
81
0
    return (CMD_RETURN_ERROR);
82
0
  }
83
84
0
  if (wait)
85
0
    cdata->item = item;
86
87
0
  cdata->default_yes = args_has(args, 'y');
88
0
  if ((confirm_key = args_get(args, 'c')) != NULL) {
89
0
    if (confirm_key[1] == '\0' &&
90
0
        confirm_key[0] > 31 &&
91
0
        confirm_key[0] < 127)
92
0
      cdata->confirm_key = confirm_key[0];
93
0
    else {
94
0
      cmdq_error(item, "invalid confirm key");
95
0
      free(cdata);
96
0
      return (CMD_RETURN_ERROR);
97
0
    }
98
0
  }
99
0
  else
100
0
    cdata->confirm_key = 'y';
101
102
0
  if ((prompt = args_get(args, 'p')) != NULL)
103
0
    xasprintf(&new_prompt, "%s ", prompt);
104
0
  else {
105
0
    cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name;
106
0
    xasprintf(&new_prompt, "Confirm '%s'? (%c/n) ", cmd,
107
0
        cdata->confirm_key);
108
0
  }
109
110
0
  status_prompt_set(tc, target, new_prompt, NULL,
111
0
      cmd_confirm_before_callback, cmd_confirm_before_free, cdata,
112
0
      PROMPT_SINGLE, PROMPT_TYPE_COMMAND);
113
0
  free(new_prompt);
114
115
0
  if (!wait)
116
0
    return (CMD_RETURN_NORMAL);
117
0
  return (CMD_RETURN_WAIT);
118
0
}
119
120
static int
121
cmd_confirm_before_callback(struct client *c, void *data, const char *s,
122
    __unused int done)
123
0
{
124
0
  struct cmd_confirm_before_data  *cdata = data;
125
0
  struct cmdq_item    *item = cdata->item, *new_item;
126
0
  int        retcode = 1;
127
128
0
  if (c->flags & CLIENT_DEAD)
129
0
    goto out;
130
131
0
  if (s == NULL)
132
0
    goto out;
133
0
  if (s[0] != cdata->confirm_key && (s[0] != '\r' || !cdata->default_yes))
134
0
    goto out;
135
0
  retcode = 0;
136
137
0
  if (item == NULL) {
138
0
    new_item = cmdq_get_command(cdata->cmdlist, NULL);
139
0
    cmdq_append(c, new_item);
140
0
  } else {
141
0
    new_item = cmdq_get_command(cdata->cmdlist,
142
0
        cmdq_get_state(item));
143
0
    cmdq_insert_after(item, new_item);
144
0
  }
145
146
0
out:
147
0
  if (item != NULL) {
148
0
    if (cmdq_get_client(item) != NULL &&
149
0
        cmdq_get_client(item)->session == NULL)
150
0
      cmdq_get_client(item)->retval = retcode;
151
0
    cmdq_continue(item);
152
0
  }
153
0
  return (0);
154
0
}
155
156
static void
157
cmd_confirm_before_free(void *data)
158
0
{
159
0
  struct cmd_confirm_before_data  *cdata = data;
160
161
0
  cmd_list_free(cdata->cmdlist);
162
0
  free(cdata);
163
0
}