Coverage Report

Created: 2026-07-16 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/cmd-command-prompt.c
Line
Count
Source
1
/* $OpenBSD: cmd-command-prompt.c,v 1.75 2026/06/25 11:39:11 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
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
#include <time.h>
25
26
#include "tmux.h"
27
28
/*
29
 * Prompt for command in client.
30
 */
31
32
static enum args_parse_type cmd_command_prompt_args_parse(struct args *,
33
            u_int, char **);
34
static enum cmd_retval    cmd_command_prompt_exec(struct cmd *,
35
            struct cmdq_item *);
36
37
static enum prompt_result cmd_command_prompt_callback(struct client *, void *,
38
        const char *, enum prompt_key_result);
39
static void cmd_command_prompt_free(void *);
40
41
const struct cmd_entry cmd_command_prompt_entry = {
42
  .name = "command-prompt",
43
  .alias = NULL,
44
45
  .args = { "1CbeFiklI:NPp:t:T:", 0, 1, cmd_command_prompt_args_parse },
46
  .usage = "[-1CbeFiklNP] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
47
     " [-T prompt-type] [template]",
48
49
  .flags = CMD_CLIENT_TFLAG,
50
  .exec = cmd_command_prompt_exec
51
};
52
53
struct cmd_command_prompt_prompt {
54
  char  *input;
55
  char  *prompt;
56
};
57
58
struct cmd_command_prompt_cdata {
59
  struct cmdq_item     *item;
60
  struct args_command_state  *state;
61
62
  int         flags;
63
  enum prompt_type      prompt_type;
64
65
  struct window_pane     *wp;
66
67
  struct cmd_command_prompt_prompt *prompts;
68
  u_int         count;
69
  u_int         current;
70
71
  int         argc;
72
  char        **argv;
73
};
74
75
static enum args_parse_type
76
cmd_command_prompt_args_parse(__unused struct args *args, __unused u_int idx,
77
    __unused char **cause)
78
0
{
79
0
  return (ARGS_PARSE_COMMANDS_OR_STRING);
80
0
}
81
82
static enum cmd_retval
83
cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
84
0
{
85
0
  struct args     *args = cmd_get_args(self);
86
0
  struct client     *tc = cmdq_get_target_client(item);
87
0
  struct cmd_find_state   *target = cmdq_get_target(item);
88
0
  const char      *type, *s, *input;
89
0
  struct cmd_command_prompt_cdata *cdata;
90
0
  char        *tmp, *prompts, *prompt, *next_prompt;
91
0
  char        *inputs = NULL, *next_input;
92
0
  struct window_pane    *wp = target->wp;
93
0
  u_int        count = args_count(args);
94
0
  int        wait = !args_has(args, 'b'), space = 1;
95
0
  int        pane = args_has(args, 'P');
96
97
0
  if (pane) {
98
0
    if (wp == NULL || window_pane_has_prompt(wp))
99
0
      return (CMD_RETURN_NORMAL);
100
0
  } else if (tc->prompt != NULL)
101
0
    return (CMD_RETURN_NORMAL);
102
0
  if (args_has(args, 'i'))
103
0
    wait = 0;
104
105
0
  cdata = xcalloc(1, sizeof *cdata);
106
0
  if (wait)
107
0
    cdata->item = item;
108
0
  if (pane)
109
0
    cdata->wp = wp;
110
0
  cdata->state = args_make_commands_prepare(self, item, 0, "%1", wait,
111
0
      args_has(args, 'F'));
112
113
0
  if ((s = args_get(args, 'p')) == NULL) {
114
0
    if (count != 0) {
115
0
      tmp = args_make_commands_get_command(cdata->state);
116
0
      xasprintf(&prompts, "(%s)", tmp);
117
0
      free(tmp);
118
0
    } else {
119
0
      prompts = xstrdup(":");
120
0
      space = 0;
121
0
    }
122
0
    next_prompt = prompts;
123
0
  } else
124
0
    next_prompt = prompts = xstrdup(s);
125
0
  if ((s = args_get(args, 'I')) != NULL)
126
0
    next_input = inputs = xstrdup(s);
127
0
  else
128
0
    next_input = NULL;
129
0
  if (args_has(args, 'l')) {
130
0
    cdata->prompts = xcalloc(1, sizeof *cdata->prompts);
131
0
    cdata->prompts[0].prompt = prompts;
132
0
    cdata->prompts[0].input = inputs;
133
0
    cdata->count = 1;
134
0
  } else {
135
0
    while ((prompt = strsep(&next_prompt, ",")) != NULL) {
136
0
      cdata->prompts = xreallocarray(cdata->prompts,
137
0
          cdata->count + 1, sizeof *cdata->prompts);
138
0
      if (!space)
139
0
        tmp = xstrdup(prompt);
140
0
      else
141
0
        xasprintf(&tmp, "%s ", prompt);
142
0
      cdata->prompts[cdata->count].prompt = tmp;
143
144
0
      if (next_input != NULL) {
145
0
        input = strsep(&next_input, ",");
146
0
        if (input == NULL)
147
0
          input = "";
148
0
      } else
149
0
        input = "";
150
0
      cdata->prompts[cdata->count].input = xstrdup(input);
151
0
      cdata->count++;
152
0
    }
153
0
    free(inputs);
154
0
    free(prompts);
155
0
  }
156
157
0
  if ((type = args_get(args, 'T')) != NULL) {
158
0
    cdata->prompt_type = prompt_type(type);
159
0
    if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
160
0
      cmdq_error(item, "unknown type: %s", type);
161
0
      cmd_command_prompt_free(cdata);
162
0
      return (CMD_RETURN_ERROR);
163
0
    }
164
0
  } else
165
0
    cdata->prompt_type = PROMPT_TYPE_COMMAND;
166
167
0
  if (args_has(args, '1'))
168
0
    cdata->flags |= PROMPT_SINGLE;
169
0
  else if (args_has(args, 'N'))
170
0
    cdata->flags |= PROMPT_NUMERIC;
171
0
  else if (args_has(args, 'i'))
172
0
    cdata->flags |= PROMPT_INCREMENTAL;
173
0
  else if (args_has(args, 'k'))
174
0
    cdata->flags |= PROMPT_KEY;
175
0
  else if (args_has(args, 'e'))
176
0
    cdata->flags |= PROMPT_BSPACE_EXIT;
177
0
  if (args_has(args, 'C'))
178
0
    cdata->flags |= PROMPT_NOFREEZE;
179
0
  if (pane) {
180
0
    cdata->flags |= PROMPT_ISPANE;
181
0
    window_pane_set_prompt(wp, tc, target, cdata->prompts[0].prompt,
182
0
        cdata->prompts[0].input, cmd_command_prompt_callback,
183
0
        cmd_command_prompt_free, cdata, cdata->flags,
184
0
        cdata->prompt_type);
185
0
  } else {
186
0
    status_prompt_set(tc, target, cdata->prompts[0].prompt,
187
0
        cdata->prompts[0].input, cmd_command_prompt_callback,
188
0
        cmd_command_prompt_free, cdata, cdata->flags,
189
0
        cdata->prompt_type);
190
0
  }
191
192
0
  if (!wait)
193
0
    return (CMD_RETURN_NORMAL);
194
0
  return (CMD_RETURN_WAIT);
195
0
}
196
197
static enum prompt_result
198
cmd_command_prompt_callback(struct client *c, void *data, const char *s,
199
    enum prompt_key_result key)
200
0
{
201
0
  struct cmd_command_prompt_cdata    *cdata = data;
202
0
  char           *error;
203
0
  struct cmdq_item       *item = cdata->item, *new_item;
204
0
  struct cmd_list        *cmdlist;
205
0
  struct cmd_command_prompt_prompt   *prompt;
206
0
  int           argc = 0;
207
0
  char          **argv = NULL;
208
209
0
  if (s == NULL || key == PROMPT_KEY_MOVE)
210
0
    goto out;
211
212
0
  if (key == PROMPT_KEY_CLOSE) {
213
0
    if (cdata->flags & PROMPT_INCREMENTAL)
214
0
      goto out;
215
0
    cmd_append_argv(&cdata->argc, &cdata->argv, s);
216
0
    if (++cdata->current != cdata->count) {
217
0
      prompt = &cdata->prompts[cdata->current];
218
0
      if (cdata->wp != NULL) {
219
0
        window_pane_update_prompt(cdata->wp,
220
0
            prompt->prompt, prompt->input);
221
0
      } else
222
0
        status_prompt_update(c, prompt->prompt,
223
0
            prompt->input);
224
0
      return (PROMPT_CONTINUE);
225
0
    }
226
0
  }
227
228
0
  argc = cdata->argc;
229
0
  argv = cmd_copy_argv(cdata->argc, cdata->argv);
230
0
  if (key != PROMPT_KEY_CLOSE)
231
0
    cmd_append_argv(&argc, &argv, s);
232
0
  else {
233
0
    cmd_free_argv(cdata->argc, cdata->argv);
234
0
    cdata->argc = argc;
235
0
    cdata->argv = cmd_copy_argv(argc, argv);
236
0
  }
237
238
0
  cmdlist = args_make_commands(cdata->state, argc, argv, &error);
239
0
  if (cmdlist == NULL) {
240
0
    cmdq_append(c, cmdq_get_error(error));
241
0
    free(error);
242
0
  } else if (item == NULL) {
243
0
    new_item = cmdq_get_command(cmdlist, NULL);
244
0
    cmdq_append(c, new_item);
245
0
  } else {
246
0
    new_item = cmdq_get_command(cmdlist, cmdq_get_state(item));
247
0
    cmdq_insert_after(item, new_item);
248
0
  }
249
0
  cmd_free_argv(argc, argv);
250
251
  /*
252
   * An incremental prompt fires its callback on every edit but must stay
253
   * open for further typing; only an explicit close (handled above) ends
254
   * it.
255
   */
256
0
  if (cdata->flags & PROMPT_INCREMENTAL)
257
0
    return (PROMPT_CONTINUE);
258
259
0
out:
260
0
  if (item != NULL) {
261
0
    cdata->item = NULL;
262
0
    cmdq_continue(item);
263
0
  }
264
0
  return (PROMPT_CLOSE);
265
0
}
266
267
static void
268
cmd_command_prompt_free(void *data)
269
0
{
270
0
  struct cmd_command_prompt_cdata *cdata = data;
271
0
  u_int        i;
272
273
0
  if (cdata->item != NULL) {
274
0
    cmdq_continue(cdata->item);
275
0
    cdata->item = NULL;
276
0
  }
277
278
0
  for (i = 0; i < cdata->count; i++) {
279
0
    free(cdata->prompts[i].prompt);
280
0
    free(cdata->prompts[i].input);
281
0
  }
282
0
  free(cdata->prompts);
283
0
  cmd_free_argv(cdata->argc, cdata->argv);
284
0
  args_make_commands_free(cdata->state);
285
0
  free(cdata);
286
0
}