Coverage Report

Created: 2026-10-28 06:08

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