Coverage Report

Created: 2025-07-12 06:33

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