Coverage Report

Created: 2026-09-13 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/cmd.c
Line
Count
Source
1
/* $OpenBSD: cmd.c,v 1.189 2026/07/17 08:29:34 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2007 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
#include <sys/time.h>
21
22
#include <fnmatch.h>
23
#include <pwd.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <unistd.h>
27
28
#include "tmux.h"
29
30
extern const struct cmd_entry cmd_attach_session_entry;
31
extern const struct cmd_entry cmd_bind_key_entry;
32
extern const struct cmd_entry cmd_break_pane_entry;
33
extern const struct cmd_entry cmd_capture_pane_entry;
34
extern const struct cmd_entry cmd_choose_buffer_entry;
35
extern const struct cmd_entry cmd_choose_client_entry;
36
extern const struct cmd_entry cmd_choose_tree_entry;
37
extern const struct cmd_entry cmd_clear_history_entry;
38
extern const struct cmd_entry cmd_clear_prompt_history_entry;
39
extern const struct cmd_entry cmd_clock_mode_entry;
40
extern const struct cmd_entry cmd_command_prompt_entry;
41
extern const struct cmd_entry cmd_confirm_before_entry;
42
extern const struct cmd_entry cmd_copy_mode_entry;
43
extern const struct cmd_entry cmd_customize_mode_entry;
44
extern const struct cmd_entry cmd_delete_buffer_entry;
45
extern const struct cmd_entry cmd_detach_client_entry;
46
extern const struct cmd_entry cmd_display_menu_entry;
47
extern const struct cmd_entry cmd_display_message_entry;
48
extern const struct cmd_entry cmd_display_popup_entry;
49
extern const struct cmd_entry cmd_display_panes_entry;
50
extern const struct cmd_entry cmd_find_window_entry;
51
extern const struct cmd_entry cmd_has_session_entry;
52
extern const struct cmd_entry cmd_if_shell_entry;
53
extern const struct cmd_entry cmd_join_pane_entry;
54
extern const struct cmd_entry cmd_kill_pane_entry;
55
extern const struct cmd_entry cmd_kill_server_entry;
56
extern const struct cmd_entry cmd_kill_session_entry;
57
extern const struct cmd_entry cmd_kill_window_entry;
58
extern const struct cmd_entry cmd_last_pane_entry;
59
extern const struct cmd_entry cmd_last_window_entry;
60
extern const struct cmd_entry cmd_link_window_entry;
61
extern const struct cmd_entry cmd_list_buffers_entry;
62
extern const struct cmd_entry cmd_list_clients_entry;
63
extern const struct cmd_entry cmd_list_commands_entry;
64
extern const struct cmd_entry cmd_list_keys_entry;
65
extern const struct cmd_entry cmd_list_panes_entry;
66
extern const struct cmd_entry cmd_list_sessions_entry;
67
extern const struct cmd_entry cmd_list_windows_entry;
68
extern const struct cmd_entry cmd_load_buffer_entry;
69
extern const struct cmd_entry cmd_lock_client_entry;
70
extern const struct cmd_entry cmd_lock_server_entry;
71
extern const struct cmd_entry cmd_lock_session_entry;
72
extern const struct cmd_entry cmd_move_pane_entry;
73
extern const struct cmd_entry cmd_move_window_entry;
74
extern const struct cmd_entry cmd_new_pane_entry;
75
extern const struct cmd_entry cmd_new_session_entry;
76
extern const struct cmd_entry cmd_new_window_entry;
77
extern const struct cmd_entry cmd_next_layout_entry;
78
extern const struct cmd_entry cmd_next_window_entry;
79
extern const struct cmd_entry cmd_paste_buffer_entry;
80
extern const struct cmd_entry cmd_pipe_pane_entry;
81
extern const struct cmd_entry cmd_previous_layout_entry;
82
extern const struct cmd_entry cmd_previous_window_entry;
83
extern const struct cmd_entry cmd_refresh_client_entry;
84
extern const struct cmd_entry cmd_rename_session_entry;
85
extern const struct cmd_entry cmd_rename_window_entry;
86
extern const struct cmd_entry cmd_resize_pane_entry;
87
extern const struct cmd_entry cmd_resize_window_entry;
88
extern const struct cmd_entry cmd_respawn_pane_entry;
89
extern const struct cmd_entry cmd_respawn_window_entry;
90
extern const struct cmd_entry cmd_rotate_window_entry;
91
extern const struct cmd_entry cmd_run_shell_entry;
92
extern const struct cmd_entry cmd_save_buffer_entry;
93
extern const struct cmd_entry cmd_select_layout_entry;
94
extern const struct cmd_entry cmd_select_pane_entry;
95
extern const struct cmd_entry cmd_select_window_entry;
96
extern const struct cmd_entry cmd_send_keys_entry;
97
extern const struct cmd_entry cmd_send_prefix_entry;
98
extern const struct cmd_entry cmd_server_access_entry;
99
extern const struct cmd_entry cmd_set_buffer_entry;
100
extern const struct cmd_entry cmd_set_environment_entry;
101
extern const struct cmd_entry cmd_set_hook_entry;
102
extern const struct cmd_entry cmd_set_option_entry;
103
extern const struct cmd_entry cmd_set_window_option_entry;
104
extern const struct cmd_entry cmd_show_buffer_entry;
105
extern const struct cmd_entry cmd_show_environment_entry;
106
extern const struct cmd_entry cmd_show_hooks_entry;
107
extern const struct cmd_entry cmd_show_messages_entry;
108
extern const struct cmd_entry cmd_show_options_entry;
109
extern const struct cmd_entry cmd_show_prompt_history_entry;
110
extern const struct cmd_entry cmd_show_window_options_entry;
111
extern const struct cmd_entry cmd_source_file_entry;
112
extern const struct cmd_entry cmd_split_window_entry;
113
extern const struct cmd_entry cmd_start_server_entry;
114
extern const struct cmd_entry cmd_suspend_client_entry;
115
extern const struct cmd_entry cmd_swap_pane_entry;
116
extern const struct cmd_entry cmd_swap_window_entry;
117
extern const struct cmd_entry cmd_switch_client_entry;
118
extern const struct cmd_entry cmd_switch_mode_entry;
119
extern const struct cmd_entry cmd_unbind_key_entry;
120
extern const struct cmd_entry cmd_unlink_window_entry;
121
extern const struct cmd_entry cmd_wait_for_entry;
122
123
const struct cmd_entry *cmd_table[] = {
124
  &cmd_attach_session_entry,
125
  &cmd_bind_key_entry,
126
  &cmd_break_pane_entry,
127
  &cmd_capture_pane_entry,
128
  &cmd_choose_buffer_entry,
129
  &cmd_choose_client_entry,
130
  &cmd_choose_tree_entry,
131
  &cmd_clear_history_entry,
132
  &cmd_clear_prompt_history_entry,
133
  &cmd_clock_mode_entry,
134
  &cmd_command_prompt_entry,
135
  &cmd_confirm_before_entry,
136
  &cmd_copy_mode_entry,
137
  &cmd_customize_mode_entry,
138
  &cmd_delete_buffer_entry,
139
  &cmd_detach_client_entry,
140
  &cmd_display_menu_entry,
141
  &cmd_display_message_entry,
142
  &cmd_display_popup_entry,
143
  &cmd_display_panes_entry,
144
  &cmd_find_window_entry,
145
  &cmd_has_session_entry,
146
  &cmd_if_shell_entry,
147
  &cmd_join_pane_entry,
148
  &cmd_kill_pane_entry,
149
  &cmd_kill_server_entry,
150
  &cmd_kill_session_entry,
151
  &cmd_kill_window_entry,
152
  &cmd_last_pane_entry,
153
  &cmd_last_window_entry,
154
  &cmd_link_window_entry,
155
  &cmd_list_buffers_entry,
156
  &cmd_list_clients_entry,
157
  &cmd_list_commands_entry,
158
  &cmd_list_keys_entry,
159
  &cmd_list_panes_entry,
160
  &cmd_list_sessions_entry,
161
  &cmd_list_windows_entry,
162
  &cmd_load_buffer_entry,
163
  &cmd_lock_client_entry,
164
  &cmd_lock_server_entry,
165
  &cmd_lock_session_entry,
166
  &cmd_move_pane_entry,
167
  &cmd_move_window_entry,
168
  &cmd_new_pane_entry,
169
  &cmd_new_session_entry,
170
  &cmd_new_window_entry,
171
  &cmd_next_layout_entry,
172
  &cmd_next_window_entry,
173
  &cmd_paste_buffer_entry,
174
  &cmd_pipe_pane_entry,
175
  &cmd_previous_layout_entry,
176
  &cmd_previous_window_entry,
177
  &cmd_refresh_client_entry,
178
  &cmd_rename_session_entry,
179
  &cmd_rename_window_entry,
180
  &cmd_resize_pane_entry,
181
  &cmd_resize_window_entry,
182
  &cmd_respawn_pane_entry,
183
  &cmd_respawn_window_entry,
184
  &cmd_rotate_window_entry,
185
  &cmd_run_shell_entry,
186
  &cmd_save_buffer_entry,
187
  &cmd_select_layout_entry,
188
  &cmd_select_pane_entry,
189
  &cmd_select_window_entry,
190
  &cmd_send_keys_entry,
191
  &cmd_send_prefix_entry,
192
  &cmd_server_access_entry,
193
  &cmd_set_buffer_entry,
194
  &cmd_set_environment_entry,
195
  &cmd_set_hook_entry,
196
  &cmd_set_option_entry,
197
  &cmd_set_window_option_entry,
198
  &cmd_show_buffer_entry,
199
  &cmd_show_environment_entry,
200
  &cmd_show_hooks_entry,
201
  &cmd_show_messages_entry,
202
  &cmd_show_options_entry,
203
  &cmd_show_prompt_history_entry,
204
  &cmd_show_window_options_entry,
205
  &cmd_source_file_entry,
206
  &cmd_split_window_entry,
207
  &cmd_start_server_entry,
208
  &cmd_suspend_client_entry,
209
  &cmd_swap_pane_entry,
210
  &cmd_swap_window_entry,
211
  &cmd_switch_client_entry,
212
  &cmd_switch_mode_entry,
213
  &cmd_unbind_key_entry,
214
  &cmd_unlink_window_entry,
215
  &cmd_wait_for_entry,
216
  NULL
217
};
218
219
/* Instance of a command. */
220
struct cmd {
221
  const struct cmd_entry   *entry;
222
  struct args    *args;
223
  u_int       group;
224
225
  char       *file;
226
  u_int       line;
227
  int       parse_flags;
228
229
  TAILQ_ENTRY(cmd)    qentry;
230
};
231
TAILQ_HEAD(cmds, cmd);
232
233
/* Next group number for new command list. */
234
static u_int cmd_list_next_group = 1;
235
236
/* Log an argument vector. */
237
void printflike(3, 4)
238
cmd_log_argv(int argc, char **argv, const char *fmt, ...)
239
0
{
240
0
  char  *prefix;
241
0
  va_list  ap;
242
0
  int  i;
243
244
0
  va_start(ap, fmt);
245
0
  xvasprintf(&prefix, fmt, ap);
246
0
  va_end(ap);
247
248
0
  for (i = 0; i < argc; i++)
249
0
    log_debug("%s: argv[%d]=%s", prefix, i, argv[i]);
250
0
  free(prefix);
251
0
}
252
253
/* Prepend to an argument vector. */
254
void
255
cmd_prepend_argv(int *argc, char ***argv, const char *arg)
256
0
{
257
0
  char  **new_argv;
258
0
  int   i;
259
260
0
  new_argv = xreallocarray(NULL, (*argc) + 1, sizeof *new_argv);
261
0
  new_argv[0] = xstrdup(arg);
262
0
  for (i = 0; i < *argc; i++)
263
0
    new_argv[1 + i] = (*argv)[i];
264
265
0
  free(*argv);
266
0
  *argv = new_argv;
267
0
  (*argc)++;
268
0
}
269
270
/* Append to an argument vector. */
271
void
272
cmd_append_argv(int *argc, char ***argv, const char *arg)
273
0
{
274
0
  *argv = xreallocarray(*argv, (*argc) + 1, sizeof **argv);
275
0
  (*argv)[(*argc)++] = xstrdup(arg);
276
0
}
277
278
/* Pack an argument vector up into a buffer. */
279
int
280
cmd_pack_argv(int argc, char **argv, char *buf, size_t len)
281
0
{
282
0
  size_t  arglen;
283
0
  int i;
284
285
0
  if (argc == 0)
286
0
    return (0);
287
0
  cmd_log_argv(argc, argv, "%s", __func__);
288
289
0
  *buf = '\0';
290
0
  for (i = 0; i < argc; i++) {
291
0
    if (strlcpy(buf, argv[i], len) >= len)
292
0
      return (-1);
293
0
    arglen = strlen(argv[i]) + 1;
294
0
    buf += arglen;
295
0
    len -= arglen;
296
0
  }
297
298
0
  return (0);
299
0
}
300
301
/* Unpack an argument vector from a packed buffer. */
302
int
303
cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
304
0
{
305
0
  int i;
306
0
  size_t  arglen;
307
308
0
  if (argc == 0)
309
0
    return (0);
310
0
  if (argc < 0 || argc > 1000)
311
0
    return (-1);
312
0
  *argv = xcalloc(argc, sizeof **argv);
313
314
0
  buf[len - 1] = '\0';
315
0
  for (i = 0; i < argc; i++) {
316
0
    if (len == 0) {
317
0
      cmd_free_argv(argc, *argv);
318
0
      return (-1);
319
0
    }
320
321
0
    arglen = strlen(buf) + 1;
322
0
    (*argv)[i] = xstrdup(buf);
323
324
0
    buf += arglen;
325
0
    len -= arglen;
326
0
  }
327
0
  cmd_log_argv(argc, *argv, "%s", __func__);
328
329
0
  return (0);
330
0
}
331
332
/* Copy an argument vector, ensuring it is terminated by NULL. */
333
char **
334
cmd_copy_argv(int argc, char **argv)
335
0
{
336
0
  char  **new_argv;
337
0
  int   i;
338
339
0
  if (argc == 0)
340
0
    return (NULL);
341
0
  new_argv = xcalloc(argc + 1, sizeof *new_argv);
342
0
  for (i = 0; i < argc; i++) {
343
0
    if (argv[i] != NULL)
344
0
      new_argv[i] = xstrdup(argv[i]);
345
0
  }
346
0
  return (new_argv);
347
0
}
348
349
/* Free an argument vector. */
350
void
351
cmd_free_argv(int argc, char **argv)
352
966k
{
353
966k
  int i;
354
355
966k
  if (argc == 0)
356
301k
    return;
357
1.57M
  for (i = 0; i < argc; i++)
358
912k
    free(argv[i]);
359
665k
  free(argv);
360
665k
}
361
362
/* Convert argument vector to a string. */
363
char *
364
cmd_stringify_argv(int argc, char **argv)
365
0
{
366
0
  char  *buf = NULL, *s;
367
0
  size_t   len = 0;
368
0
  int  i;
369
370
0
  if (argc == 0)
371
0
    return (xstrdup(""));
372
373
0
  for (i = 0; i < argc; i++) {
374
0
    s = args_escape(argv[i]);
375
0
    log_debug("%s: %u %s = %s", __func__, i, argv[i], s);
376
377
0
    len += strlen(s) + 1;
378
0
    buf = xrealloc(buf, len);
379
380
0
    if (i == 0)
381
0
      *buf = '\0';
382
0
    else
383
0
      strlcat(buf, " ", len);
384
0
    strlcat(buf, s, len);
385
386
0
    free(s);
387
0
  }
388
0
  return (buf);
389
0
}
390
391
/* Get entry for command. */
392
const struct cmd_entry *
393
cmd_get_entry(struct cmd *cmd)
394
0
{
395
0
  return (cmd->entry);
396
0
}
397
398
/* Get arguments for command. */
399
struct args *
400
cmd_get_args(struct cmd *cmd)
401
0
{
402
0
  return (cmd->args);
403
0
}
404
405
/* Get group for command. */
406
u_int
407
cmd_get_group(struct cmd *cmd)
408
0
{
409
0
  return (cmd->group);
410
0
}
411
412
/* Get file and line for command. */
413
void
414
cmd_get_source(struct cmd *cmd, const char **file, u_int *line)
415
0
{
416
0
  if (file != NULL)
417
0
    *file = cmd->file;
418
0
  if (line != NULL)
419
0
    *line = cmd->line;
420
0
}
421
422
/* Get parse flags for command. */
423
int
424
cmd_get_parse_flags(struct cmd *cmd)
425
0
{
426
0
  return (cmd->parse_flags);
427
0
}
428
429
/* Look for an alias for a command. */
430
char *
431
cmd_get_alias(const char *name)
432
1.62k
{
433
1.62k
  struct options_entry    *o;
434
1.62k
  struct options_array_item *a;
435
1.62k
  union options_value   *ov;
436
1.62k
  size_t         wanted, n;
437
1.62k
  const char      *equals;
438
439
1.62k
  o = options_get_only(global_options, "command-alias");
440
1.62k
  if (o == NULL)
441
0
    return (NULL);
442
1.62k
  wanted = strlen(name);
443
444
1.62k
  a = options_array_first(o);
445
10.9k
  while (a != NULL) {
446
9.48k
    ov = options_array_item_value(a);
447
448
9.48k
    equals = strchr(ov->string, '=');
449
9.48k
    if (equals != NULL) {
450
9.48k
      n = equals - ov->string;
451
9.48k
      if (n == wanted && strncmp(name, ov->string, n) == 0)
452
128
        return (xstrdup(equals + 1));
453
9.48k
    }
454
455
9.36k
    a = options_array_next(a);
456
9.36k
  }
457
1.49k
  return (NULL);
458
1.62k
}
459
460
/* Look up a command entry by name. */
461
const struct cmd_entry *
462
cmd_find(const char *name, char **cause)
463
1.62k
{
464
1.62k
  const struct cmd_entry  **loop, *entry, *found = NULL;
465
1.62k
  int       ambiguous;
466
1.62k
  char        s[8192];
467
468
1.62k
  ambiguous = 0;
469
113k
  for (loop = cmd_table; *loop != NULL; loop++) {
470
113k
    entry = *loop;
471
113k
    if (entry->alias != NULL && strcmp(entry->alias, name) == 0) {
472
1.31k
      ambiguous = 0;
473
1.31k
      found = entry;
474
1.31k
      break;
475
1.31k
    }
476
477
112k
    if (strncmp(entry->name, name, strlen(name)) != 0)
478
108k
      continue;
479
3.76k
    if (found != NULL)
480
2.32k
      ambiguous = 1;
481
3.76k
    found = entry;
482
483
3.76k
    if (strcmp(entry->name, name) == 0)
484
136
      break;
485
3.76k
  }
486
1.62k
  if (ambiguous)
487
1
    goto ambiguous;
488
1.62k
  if (found == NULL) {
489
25
    xasprintf(cause, "unknown command: %s", name);
490
25
    return (NULL);
491
25
  }
492
1.59k
  return (found);
493
494
1
ambiguous:
495
1
  *s = '\0';
496
93
  for (loop = cmd_table; *loop != NULL; loop++) {
497
92
    entry = *loop;
498
92
    if (strncmp(entry->name, name, strlen(name)) != 0)
499
90
      continue;
500
2
    if (strlcat(s, entry->name, sizeof s) >= sizeof s)
501
0
      break;
502
2
    if (strlcat(s, ", ", sizeof s) >= sizeof s)
503
0
      break;
504
2
  }
505
1
  s[strlen(s) - 2] = '\0';
506
1
  xasprintf(cause, "ambiguous command: %s, could be: %s", name, s);
507
1
  return (NULL);
508
1.62k
}
509
510
/* Parse a single command from an argument vector. */
511
struct cmd *
512
cmd_parse(struct args_value *values, u_int count, const char *file, u_int line,
513
    int parse_flags, char **cause)
514
1.62k
{
515
1.62k
  const struct cmd_entry  *entry;
516
1.62k
  struct cmd    *cmd;
517
1.62k
  struct args   *args;
518
1.62k
  char      *error = NULL;
519
520
1.62k
  if (count == 0 || values[0].type != ARGS_STRING) {
521
0
    xasprintf(cause, "no command");
522
0
    return (NULL);
523
0
  }
524
1.62k
  entry = cmd_find(values[0].string, cause);
525
1.62k
  if (entry == NULL)
526
26
    return (NULL);
527
528
1.59k
  args = args_parse(&entry->args, values, count, &error);
529
1.59k
  if (args == NULL && error == NULL) {
530
0
    xasprintf(cause, "usage: %s %s", entry->name, entry->usage);
531
0
    return (NULL);
532
0
  }
533
1.59k
  if (args == NULL) {
534
17
    xasprintf(cause, "command %s: %s", entry->name, error);
535
17
    free(error);
536
17
    return (NULL);
537
17
  }
538
539
1.57k
  cmd = xcalloc(1, sizeof *cmd);
540
1.57k
  cmd->entry = entry;
541
1.57k
  cmd->args = args;
542
1.57k
  cmd->parse_flags = parse_flags;
543
544
1.57k
  if (file != NULL)
545
0
    cmd->file = xstrdup(file);
546
1.57k
  cmd->line = line;
547
548
1.57k
  return (cmd);
549
1.59k
}
550
551
/* Free a command. */
552
void
553
cmd_free(struct cmd *cmd)
554
1.57k
{
555
1.57k
  free(cmd->file);
556
557
1.57k
  args_free(cmd->args);
558
1.57k
  free(cmd);
559
1.57k
}
560
561
/* Copy a command. */
562
struct cmd *
563
cmd_copy(struct cmd *cmd, int argc, char **argv)
564
0
{
565
0
  struct cmd  *new_cmd;
566
567
0
  new_cmd = xcalloc(1, sizeof *new_cmd);
568
0
  new_cmd->entry = cmd->entry;
569
0
  new_cmd->args = args_copy(cmd->args, argc, argv);
570
571
0
  if (cmd->file != NULL)
572
0
    new_cmd->file = xstrdup(cmd->file);
573
0
  new_cmd->line = cmd->line;
574
575
0
  return (new_cmd);
576
0
}
577
578
/* Get a command as a string. */
579
char *
580
cmd_print(struct cmd *cmd)
581
1.27k
{
582
1.27k
  char  *out, *s;
583
584
1.27k
  s = args_print(cmd->args);
585
1.27k
  if (*s != '\0')
586
869
    xasprintf(&out, "%s %s", cmd->entry->name, s);
587
409
  else
588
409
    out = xstrdup(cmd->entry->name);
589
1.27k
  free(s);
590
591
1.27k
  return (out);
592
1.27k
}
593
594
/* Create a new command list. */
595
struct cmd_list *
596
cmd_list_new(void)
597
3.50k
{
598
3.50k
  struct cmd_list *cmdlist;
599
600
3.50k
  cmdlist = xcalloc(1, sizeof *cmdlist);
601
3.50k
  cmdlist->references = 1;
602
3.50k
  cmdlist->group = cmd_list_next_group++;
603
3.50k
  cmdlist->list = xcalloc(1, sizeof *cmdlist->list);
604
3.50k
  TAILQ_INIT(cmdlist->list);
605
3.50k
  return (cmdlist);
606
3.50k
}
607
608
/* Append a command to a command list. */
609
void
610
cmd_list_append(struct cmd_list *cmdlist, struct cmd *cmd)
611
1.57k
{
612
1.57k
  cmd->group = cmdlist->group;
613
1.57k
  TAILQ_INSERT_TAIL(cmdlist->list, cmd, qentry);
614
1.57k
}
615
616
/* Append all commands from one list to another.  */
617
void
618
cmd_list_append_all(struct cmd_list *cmdlist, struct cmd_list *from)
619
1.70k
{
620
1.70k
  struct cmd  *cmd;
621
622
1.70k
  TAILQ_FOREACH(cmd, from->list, qentry)
623
1.70k
    cmd->group = cmdlist->group;
624
1.70k
  TAILQ_CONCAT(cmdlist->list, from->list, qentry);
625
1.70k
}
626
627
/* Move all commands from one command list to another. */
628
void
629
cmd_list_move(struct cmd_list *cmdlist, struct cmd_list *from)
630
1.52k
{
631
1.52k
  TAILQ_CONCAT(cmdlist->list, from->list, qentry);
632
1.52k
  cmdlist->group = cmd_list_next_group++;
633
1.52k
}
634
635
/* Free a command list. */
636
void
637
cmd_list_free(struct cmd_list *cmdlist)
638
3.63k
{
639
3.63k
  struct cmd  *cmd, *cmd1;
640
641
3.63k
  if (--cmdlist->references != 0)
642
139
    return;
643
644
3.49k
  TAILQ_FOREACH_SAFE(cmd, cmdlist->list, qentry, cmd1) {
645
1.57k
    TAILQ_REMOVE(cmdlist->list, cmd, qentry);
646
1.57k
    cmd_free(cmd);
647
1.57k
  }
648
3.49k
  free(cmdlist->list);
649
3.49k
  free(cmdlist);
650
3.49k
}
651
652
/* Copy a command list, expanding %s in arguments. */
653
struct cmd_list *
654
cmd_list_copy(const struct cmd_list *cmdlist, int argc, char **argv)
655
0
{
656
0
  struct cmd  *cmd;
657
0
  struct cmd_list *new_cmdlist;
658
0
  struct cmd  *new_cmd;
659
0
  u_int    group = cmdlist->group;
660
0
  char    *s;
661
662
0
  s = cmd_list_print(cmdlist, 0);
663
0
  log_debug("%s: %s", __func__, s);
664
0
  free(s);
665
666
0
  new_cmdlist = cmd_list_new();
667
0
  TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
668
0
    if (cmd->group != group) {
669
0
      new_cmdlist->group = cmd_list_next_group++;
670
0
      group = cmd->group;
671
0
    }
672
0
    new_cmd = cmd_copy(cmd, argc, argv);
673
0
    cmd_list_append(new_cmdlist, new_cmd);
674
0
  }
675
676
0
  s = cmd_list_print(new_cmdlist, 0);
677
0
  log_debug("%s: %s", __func__, s);
678
0
  free(s);
679
680
0
  return (new_cmdlist);
681
0
}
682
683
/* Get a command list as a string. */
684
char *
685
cmd_list_print(const struct cmd_list *cmdlist, int flags)
686
879
{
687
879
  struct cmd  *cmd, *next;
688
879
  char    *buf, *this;
689
879
  size_t     len;
690
879
  const char  *separator;
691
879
  int    escaped = flags & CMD_LIST_PRINT_ESCAPED;
692
879
  int    no_groups = flags & CMD_LIST_PRINT_NO_GROUPS;
693
879
  const char  *single_separator = escaped ? " \\; " : " ; ";
694
879
  const char  *double_separator = escaped ? " \\;\\; " : " ;; ";
695
696
879
  len = 1;
697
879
  buf = xcalloc(1, len);
698
699
1.27k
  TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
700
1.27k
    this = cmd_print(cmd);
701
702
1.27k
    len += strlen(this) + 6;
703
1.27k
    buf = xrealloc(buf, len);
704
705
1.27k
    strlcat(buf, this, len);
706
707
1.27k
    next = TAILQ_NEXT(cmd, qentry);
708
1.27k
    if (next != NULL) {
709
665
      if (!no_groups && cmd->group != next->group)
710
665
        separator = double_separator;
711
0
      else
712
0
        separator = single_separator;
713
665
      strlcat(buf, separator, len);
714
665
    }
715
716
1.27k
    free(this);
717
1.27k
  }
718
719
879
  return (buf);
720
879
}
721
722
/* Get first command in list. */
723
struct cmd *
724
cmd_list_first(struct cmd_list *cmdlist)
725
0
{
726
0
  return (TAILQ_FIRST(cmdlist->list));
727
0
}
728
729
/* Get next command in list. */
730
struct cmd *
731
cmd_list_next(struct cmd *cmd)
732
0
{
733
0
  return (TAILQ_NEXT(cmd, qentry));
734
0
}
735
736
/* Do all of the commands in this command list have this flag? */
737
int
738
cmd_list_all_have(struct cmd_list *cmdlist, int flag)
739
0
{
740
0
  struct cmd  *cmd;
741
742
0
  TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
743
0
    if (~cmd->entry->flags & flag)
744
0
      return (0);
745
0
  }
746
0
  return (1);
747
0
}
748
749
/* Do any of the commands in this command list have this flag? */
750
int
751
cmd_list_any_have(struct cmd_list *cmdlist, int flag)
752
0
{
753
0
  struct cmd  *cmd;
754
755
0
  TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
756
0
    if (cmd->entry->flags & flag)
757
0
      return (1);
758
0
  }
759
0
  return (0);
760
0
}
761
762
/* Adjust current mouse position for a pane. */
763
int
764
cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
765
    u_int *yp, int last)
766
0
{
767
0
  u_int x, y;
768
769
0
  if (last) {
770
0
    x = m->lx + m->ox;
771
0
    y = m->ly + m->oy;
772
0
  } else {
773
0
    x = m->x + m->ox;
774
0
    y = m->y + m->oy;
775
0
  }
776
0
  log_debug("%s: x=%u, y=%u%s", __func__, x, y, last ? " (last)" : "");
777
778
0
  if (m->statusat == 0 && y >= m->statuslines)
779
0
    y -= m->statuslines;
780
781
0
  if ((int)x < wp->xoff || (int)x >= wp->xoff + (int)wp->sx)
782
0
    return (-1);
783
0
  if ((int)y < wp->yoff || (int)y >= wp->yoff + (int)wp->sy)
784
0
    return (-1);
785
786
0
  if (xp != NULL)
787
0
    *xp = x - wp->xoff;
788
0
  if (yp != NULL)
789
0
    *yp = y - wp->yoff;
790
0
  return (0);
791
0
}
792
793
/* Get current mouse window if any. */
794
struct winlink *
795
cmd_mouse_window(struct mouse_event *m, struct session **sp)
796
0
{
797
0
  struct session  *s;
798
0
  struct window *w;
799
0
  struct winlink  *wl;
800
801
0
  if (!m->valid)
802
0
    return (NULL);
803
0
  if (m->s == -1 || (s = session_find_by_id(m->s)) == NULL)
804
0
    return (NULL);
805
0
  if (m->w == -1)
806
0
    wl = s->curw;
807
0
  else {
808
0
    if ((w = window_find_by_id(m->w)) == NULL)
809
0
      return (NULL);
810
0
    wl = winlink_find_by_window(&s->windows, w);
811
0
  }
812
0
  if (sp != NULL)
813
0
    *sp = s;
814
0
  return (wl);
815
0
}
816
817
/* Get current mouse pane if any. */
818
struct window_pane *
819
cmd_mouse_pane(struct mouse_event *m, struct session **sp,
820
    struct winlink **wlp)
821
0
{
822
0
  struct winlink    *wl;
823
0
  struct window_pane      *wp;
824
825
0
  if ((wl = cmd_mouse_window(m, sp)) == NULL)
826
0
    return (NULL);
827
0
  if (m->wp == -1)
828
0
    wp = wl->window->active;
829
0
  else {
830
0
    if ((wp = window_pane_find_by_id(m->wp)) == NULL)
831
0
      return (NULL);
832
0
    if (!window_has_pane(wl->window, wp))
833
0
      return (NULL);
834
0
  }
835
0
  if (wl->window->modal != NULL && wp != wl->window->modal)
836
0
    return (NULL);
837
838
0
  if (wlp != NULL)
839
0
    *wlp = wl;
840
0
  return (wp);
841
0
}
842
843
/*
844
 * Replace the first %% or any %idx in template by s. %% is intended for use in
845
 * single quotes, so ' is escaped. %%% and %idx% are for double quotes, so a
846
 * list of special characters is escaped. %idx is left unescaped.
847
 */
848
char *
849
cmd_template_replace(const char *template, const char *s, int idx)
850
0
{
851
0
  char       ch, *buf;
852
0
  const char    *ptr, *cp, dquote[] = "\"\\$;~";
853
0
  int      replaced;
854
0
  size_t       len, slen;
855
0
  enum { NQ, SQ, DQ }  quote;
856
857
0
  if (strchr(template, '%') == NULL)
858
0
    return (xstrdup(template));
859
860
0
  buf = xmalloc(1);
861
0
  *buf = '\0';
862
0
  len = 0;
863
0
  replaced = 0;
864
865
0
  ptr = template;
866
0
  while (*ptr != '\0') {
867
0
    switch (ch = *ptr++) {
868
0
    case '%':
869
0
      if (*ptr >= '1' && *ptr <= '9' && *ptr - '0' == idx) {
870
0
        ptr++;
871
0
        quote = NQ;
872
0
        if (*ptr == '%') {
873
0
          quote = DQ;
874
0
          ptr++;
875
0
        }
876
0
      } else {
877
0
        if (*ptr != '%' || replaced)
878
0
          break;
879
0
        replaced = 1;
880
0
        ptr++;
881
0
        quote = SQ;
882
0
        if (*ptr == '%') {
883
0
          quote = DQ;
884
0
          ptr++;
885
0
        }
886
0
      }
887
888
0
      slen = strlen(s);
889
0
      if (slen >= SIZE_MAX / 4 ||
890
0
          len > SIZE_MAX - (slen * 4) - 1)
891
0
        fatalx("argument too long");
892
0
      buf = xrealloc(buf, len + (slen * 4) + 1);
893
0
      for (cp = s; *cp != '\0'; cp++) {
894
0
        if (quote == SQ && *cp == '\'') {
895
0
          buf[len++] = '\'';
896
0
          buf[len++] = '\\';
897
0
          buf[len++] = '\'';
898
0
          buf[len++] = '\'';
899
0
          continue;
900
0
        }
901
0
        if (quote == DQ && strchr(dquote, *cp) != NULL)
902
0
          buf[len++] = '\\';
903
0
        buf[len++] = *cp;
904
0
      }
905
0
      buf[len] = '\0';
906
0
      continue;
907
0
    }
908
0
    if (len > SIZE_MAX - 2)
909
0
      fatalx("argument too long");
910
0
    buf = xrealloc(buf, len + 2);
911
0
    buf[len++] = ch;
912
0
    buf[len] = '\0';
913
0
  }
914
915
0
  log_debug("%s: %s -> %s", __func__, template, buf);
916
0
  return (buf);
917
0
}