Coverage Report

Created: 2026-06-10 06:30

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