Coverage Report

Created: 2026-01-09 06:48

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