1 /* $OpenBSD: cmd.c,v 1.36 2009/12/08 07:49:31 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 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 <paths.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "tmux.h" 29 30 const struct cmd_entry *cmd_table[] = { 31 &cmd_attach_session_entry, 32 &cmd_bind_key_entry, 33 &cmd_break_pane_entry, 34 &cmd_capture_pane_entry, 35 &cmd_choose_client_entry, 36 &cmd_choose_session_entry, 37 &cmd_choose_window_entry, 38 &cmd_clear_history_entry, 39 &cmd_clock_mode_entry, 40 &cmd_command_prompt_entry, 41 &cmd_confirm_before_entry, 42 &cmd_copy_buffer_entry, 43 &cmd_copy_mode_entry, 44 &cmd_delete_buffer_entry, 45 &cmd_detach_client_entry, 46 &cmd_display_message_entry, 47 &cmd_display_panes_entry, 48 &cmd_down_pane_entry, 49 &cmd_find_window_entry, 50 &cmd_has_session_entry, 51 &cmd_if_shell_entry, 52 &cmd_kill_pane_entry, 53 &cmd_kill_server_entry, 54 &cmd_kill_session_entry, 55 &cmd_kill_window_entry, 56 &cmd_last_window_entry, 57 &cmd_link_window_entry, 58 &cmd_list_buffers_entry, 59 &cmd_list_clients_entry, 60 &cmd_list_commands_entry, 61 &cmd_list_keys_entry, 62 &cmd_list_panes_entry, 63 &cmd_list_sessions_entry, 64 &cmd_list_windows_entry, 65 &cmd_load_buffer_entry, 66 &cmd_lock_client_entry, 67 &cmd_lock_server_entry, 68 &cmd_lock_session_entry, 69 &cmd_move_window_entry, 70 &cmd_new_session_entry, 71 &cmd_new_window_entry, 72 &cmd_next_layout_entry, 73 &cmd_next_window_entry, 74 &cmd_paste_buffer_entry, 75 &cmd_pipe_pane_entry, 76 &cmd_previous_layout_entry, 77 &cmd_previous_window_entry, 78 &cmd_refresh_client_entry, 79 &cmd_rename_session_entry, 80 &cmd_rename_window_entry, 81 &cmd_resize_pane_entry, 82 &cmd_respawn_window_entry, 83 &cmd_rotate_window_entry, 84 &cmd_run_shell_entry, 85 &cmd_save_buffer_entry, 86 &cmd_select_layout_entry, 87 &cmd_select_pane_entry, 88 &cmd_select_prompt_entry, 89 &cmd_select_window_entry, 90 &cmd_send_keys_entry, 91 &cmd_send_prefix_entry, 92 &cmd_server_info_entry, 93 &cmd_set_buffer_entry, 94 &cmd_set_environment_entry, 95 &cmd_set_option_entry, 96 &cmd_set_window_option_entry, 97 &cmd_show_buffer_entry, 98 &cmd_show_environment_entry, 99 &cmd_show_messages_entry, 100 &cmd_show_options_entry, 101 &cmd_show_window_options_entry, 102 &cmd_source_file_entry, 103 &cmd_split_window_entry, 104 &cmd_start_server_entry, 105 &cmd_suspend_client_entry, 106 &cmd_swap_pane_entry, 107 &cmd_swap_window_entry, 108 &cmd_switch_client_entry, 109 &cmd_unbind_key_entry, 110 &cmd_unlink_window_entry, 111 &cmd_up_pane_entry, 112 NULL 113 }; 114 115 struct session *cmd_choose_session(struct sessions *); 116 struct client *cmd_choose_client(struct clients *); 117 struct client *cmd_lookup_client(const char *); 118 struct session *cmd_lookup_session(const char *, int *); 119 struct winlink *cmd_lookup_window(struct session *, const char *, int *); 120 int cmd_lookup_index(struct session *, const char *, int *); 121 122 int 123 cmd_pack_argv(int argc, char **argv, char *buf, size_t len) 124 { 125 size_t arglen; 126 int i; 127 128 *buf = '\0'; 129 for (i = 0; i < argc; i++) { 130 if (strlcpy(buf, argv[i], len) >= len) 131 return (-1); 132 arglen = strlen(argv[i]) + 1; 133 buf += arglen; 134 len -= arglen; 135 } 136 137 return (0); 138 } 139 140 int 141 cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv) 142 { 143 int i; 144 size_t arglen; 145 146 if (argc == 0) 147 return (0); 148 *argv = xcalloc(argc, sizeof **argv); 149 150 buf[len - 1] = '\0'; 151 for (i = 0; i < argc; i++) { 152 if (len == 0) { 153 cmd_free_argv(argc, *argv); 154 return (-1); 155 } 156 157 arglen = strlen(buf) + 1; 158 (*argv)[i] = xstrdup(buf); 159 buf += arglen; 160 len -= arglen; 161 } 162 163 return (0); 164 } 165 166 void 167 cmd_free_argv(int argc, char **argv) 168 { 169 int i; 170 171 if (argc == 0) 172 return; 173 for (i = 0; i < argc; i++) { 174 if (argv[i] != NULL) 175 xfree(argv[i]); 176 } 177 xfree(argv); 178 } 179 180 struct cmd * 181 cmd_parse(int argc, char **argv, char **cause) 182 { 183 const struct cmd_entry **entryp, *entry; 184 struct cmd *cmd; 185 char s[BUFSIZ]; 186 int opt, ambiguous = 0; 187 188 *cause = NULL; 189 if (argc == 0) { 190 xasprintf(cause, "no command"); 191 return (NULL); 192 } 193 194 entry = NULL; 195 for (entryp = cmd_table; *entryp != NULL; entryp++) { 196 if ((*entryp)->alias != NULL && 197 strcmp((*entryp)->alias, argv[0]) == 0) { 198 ambiguous = 0; 199 entry = *entryp; 200 break; 201 } 202 203 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0) 204 continue; 205 if (entry != NULL) 206 ambiguous = 1; 207 entry = *entryp; 208 209 /* Bail now if an exact match. */ 210 if (strcmp(entry->name, argv[0]) == 0) 211 break; 212 } 213 if (ambiguous) 214 goto ambiguous; 215 if (entry == NULL) { 216 xasprintf(cause, "unknown command: %s", argv[0]); 217 return (NULL); 218 } 219 220 optreset = 1; 221 optind = 1; 222 if (entry->parse == NULL) { 223 while ((opt = getopt(argc, argv, "")) != -1) { 224 switch (opt) { 225 default: 226 goto usage; 227 } 228 } 229 argc -= optind; 230 argv += optind; 231 if (argc != 0) 232 goto usage; 233 } 234 235 cmd = xmalloc(sizeof *cmd); 236 cmd->entry = entry; 237 cmd->data = NULL; 238 if (entry->parse != NULL) { 239 if (entry->parse(cmd, argc, argv, cause) != 0) { 240 xfree(cmd); 241 return (NULL); 242 } 243 } 244 return (cmd); 245 246 ambiguous: 247 *s = '\0'; 248 for (entryp = cmd_table; *entryp != NULL; entryp++) { 249 if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0) 250 continue; 251 if (strlcat(s, (*entryp)->name, sizeof s) >= sizeof s) 252 break; 253 if (strlcat(s, ", ", sizeof s) >= sizeof s) 254 break; 255 } 256 s[strlen(s) - 2] = '\0'; 257 xasprintf(cause, "ambiguous command: %s, could be: %s", argv[0], s); 258 return (NULL); 259 260 usage: 261 xasprintf(cause, "usage: %s %s", entry->name, entry->usage); 262 return (NULL); 263 } 264 265 int 266 cmd_exec(struct cmd *cmd, struct cmd_ctx *ctx) 267 { 268 return (cmd->entry->exec(cmd, ctx)); 269 } 270 271 void 272 cmd_free(struct cmd *cmd) 273 { 274 if (cmd->data != NULL && cmd->entry->free != NULL) 275 cmd->entry->free(cmd); 276 xfree(cmd); 277 } 278 279 size_t 280 cmd_print(struct cmd *cmd, char *buf, size_t len) 281 { 282 if (cmd->entry->print == NULL) 283 return (xsnprintf(buf, len, "%s", cmd->entry->name)); 284 return (cmd->entry->print(cmd, buf, len)); 285 } 286 287 /* 288 * Figure out the current session. Use: 1) the current session, if the command 289 * context has one; 2) the most recently used session containing the pty of the 290 * calling client, if any; 3) the session specified in the TMUX variable from 291 * the environment (as passed from the client); 4) the most recently used 292 * session from all sessions. 293 */ 294 struct session * 295 cmd_current_session(struct cmd_ctx *ctx) 296 { 297 struct msg_command_data *data = ctx->msgdata; 298 struct client *c = ctx->cmdclient; 299 struct session *s; 300 struct sessions ss; 301 struct winlink *wl; 302 struct window_pane *wp; 303 u_int i; 304 int found; 305 306 if (ctx->curclient != NULL && ctx->curclient->session != NULL) 307 return (ctx->curclient->session); 308 309 /* 310 * If the name of the calling client's pty is know, build a list of the 311 * sessions that contain it and if any choose either the first or the 312 * newest. 313 */ 314 if (c != NULL && c->tty.path != NULL) { 315 ARRAY_INIT(&ss); 316 for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 317 if ((s = ARRAY_ITEM(&sessions, i)) == NULL) 318 continue; 319 found = 0; 320 RB_FOREACH(wl, winlinks, &s->windows) { 321 TAILQ_FOREACH(wp, &wl->window->panes, entry) { 322 if (strcmp(wp->tty, c->tty.path) == 0) { 323 found = 1; 324 break; 325 } 326 } 327 if (found) 328 break; 329 } 330 if (found) 331 ARRAY_ADD(&ss, s); 332 } 333 334 s = cmd_choose_session(&ss); 335 ARRAY_FREE(&ss); 336 if (s != NULL) 337 return (s); 338 } 339 340 /* Use the session from the TMUX environment variable. */ 341 if (data != NULL && data->pid != -1) { 342 if (data->pid != getpid()) 343 return (NULL); 344 if (data->idx > ARRAY_LENGTH(&sessions)) 345 return (NULL); 346 if ((s = ARRAY_ITEM(&sessions, data->idx)) == NULL) 347 return (NULL); 348 return (s); 349 } 350 351 return (cmd_choose_session(&sessions)); 352 } 353 354 /* Find the most recently used session from a list. */ 355 struct session * 356 cmd_choose_session(struct sessions *ss) 357 { 358 struct session *s, *sbest; 359 struct timeval *tv = NULL; 360 u_int i; 361 362 sbest = NULL; 363 for (i = 0; i < ARRAY_LENGTH(ss); i++) { 364 if ((s = ARRAY_ITEM(ss, i)) == NULL) 365 continue; 366 367 if (tv == NULL || timercmp(&s->activity_time, tv, >)) { 368 sbest = s; 369 tv = &s->activity_time; 370 } 371 } 372 373 return (sbest); 374 } 375 376 /* 377 * Find the current client. First try the current client if set, then pick the 378 * most recently used of the clients attached to the current session if any, 379 * then of all clients. 380 */ 381 struct client * 382 cmd_current_client(struct cmd_ctx *ctx) 383 { 384 struct session *s; 385 struct client *c; 386 struct clients cc; 387 u_int i; 388 389 if (ctx->curclient != NULL) 390 return (ctx->curclient); 391 392 /* 393 * No current client set. Find the current session and return the 394 * newest of its clients. 395 */ 396 s = cmd_current_session(ctx); 397 if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { 398 ARRAY_INIT(&cc); 399 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 400 if ((c = ARRAY_ITEM(&clients, i)) == NULL) 401 continue; 402 if (s == c->session) 403 ARRAY_ADD(&cc, c); 404 } 405 406 c = cmd_choose_client(&cc); 407 ARRAY_FREE(&cc); 408 if (c != NULL) 409 return (c); 410 } 411 412 return (cmd_choose_client(&clients)); 413 } 414 415 /* Choose the most recently used client from a list. */ 416 struct client * 417 cmd_choose_client(struct clients *cc) 418 { 419 struct client *c, *cbest; 420 struct timeval *tv = NULL; 421 u_int i; 422 423 cbest = NULL; 424 for (i = 0; i < ARRAY_LENGTH(cc); i++) { 425 if ((c = ARRAY_ITEM(cc, i)) == NULL) 426 continue; 427 if (c->session == NULL) 428 continue; 429 430 if (tv == NULL || timercmp(&c->activity_time, tv, >)) { 431 cbest = c; 432 tv = &c->activity_time; 433 } 434 } 435 436 return (cbest); 437 } 438 439 /* Find the target client or report an error and return NULL. */ 440 struct client * 441 cmd_find_client(struct cmd_ctx *ctx, const char *arg) 442 { 443 struct client *c; 444 char *tmparg; 445 size_t arglen; 446 447 /* A NULL argument means the current client. */ 448 if (arg == NULL) 449 return (cmd_current_client(ctx)); 450 tmparg = xstrdup(arg); 451 452 /* Trim a single trailing colon if any. */ 453 arglen = strlen(tmparg); 454 if (arglen != 0 && tmparg[arglen - 1] == ':') 455 tmparg[arglen - 1] = '\0'; 456 457 /* Find the client, if any. */ 458 c = cmd_lookup_client(tmparg); 459 460 /* If no client found, report an error. */ 461 if (c == NULL) 462 ctx->error(ctx, "client not found: %s", tmparg); 463 464 xfree(tmparg); 465 return (c); 466 } 467 468 /* 469 * Lookup a client by device path. Either of a full match and a match without a 470 * leading _PATH_DEV ("/dev/") is accepted. 471 */ 472 struct client * 473 cmd_lookup_client(const char *name) 474 { 475 struct client *c; 476 const char *path; 477 u_int i; 478 479 for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 480 c = ARRAY_ITEM(&clients, i); 481 if (c == NULL || c->session == NULL) 482 continue; 483 path = c->tty.path; 484 485 /* Check for exact matches. */ 486 if (strcmp(name, path) == 0) 487 return (c); 488 489 /* Check without leading /dev if present. */ 490 if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0) 491 continue; 492 if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0) 493 return (c); 494 } 495 496 return (NULL); 497 } 498 499 /* Lookup a session by name. If no session is found, NULL is returned. */ 500 struct session * 501 cmd_lookup_session(const char *name, int *ambiguous) 502 { 503 struct session *s, *sfound; 504 u_int i; 505 506 *ambiguous = 0; 507 508 /* 509 * Look for matches. First look for exact matches - session names must 510 * be unique so an exact match can't be ambigious and can just be 511 * returned. 512 */ 513 for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 514 if ((s = ARRAY_ITEM(&sessions, i)) == NULL) 515 continue; 516 if (strcmp(name, s->name) == 0) 517 return (s); 518 } 519 520 /* 521 * Otherwise look for partial matches, returning early if it is found to 522 * be ambiguous. 523 */ 524 sfound = NULL; 525 for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 526 if ((s = ARRAY_ITEM(&sessions, i)) == NULL) 527 continue; 528 if (strncmp(name, s->name, strlen(name)) == 0 || 529 fnmatch(name, s->name, 0) == 0) { 530 if (sfound != NULL) { 531 *ambiguous = 1; 532 return (NULL); 533 } 534 sfound = s; 535 } 536 } 537 return (sfound); 538 } 539 540 /* 541 * Lookup a window or return -1 if not found or ambigious. First try as an 542 * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in 543 * idx if the window index is a valid number but there is now window with that 544 * index. 545 */ 546 struct winlink * 547 cmd_lookup_window(struct session *s, const char *name, int *ambiguous) 548 { 549 struct winlink *wl, *wlfound; 550 const char *errstr; 551 u_int idx; 552 553 *ambiguous = 0; 554 555 /* First see if this is a valid window index in this session. */ 556 idx = strtonum(name, 0, INT_MAX, &errstr); 557 if (errstr == NULL) { 558 if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL) 559 return (wl); 560 } 561 562 /* Look for exact matches, error if more than one. */ 563 wlfound = NULL; 564 RB_FOREACH(wl, winlinks, &s->windows) { 565 if (strcmp(name, wl->window->name) == 0) { 566 if (wlfound != NULL) { 567 *ambiguous = 1; 568 return (NULL); 569 } 570 wlfound = wl; 571 } 572 } 573 if (wlfound != NULL) 574 return (wlfound); 575 576 /* Now look for pattern matches, again error if multiple. */ 577 wlfound = NULL; 578 RB_FOREACH(wl, winlinks, &s->windows) { 579 if (strncmp(name, wl->window->name, strlen(name)) == 0 || 580 fnmatch(name, wl->window->name, 0) == 0) { 581 if (wlfound != NULL) { 582 *ambiguous = 1; 583 return (NULL); 584 } 585 wlfound = wl; 586 } 587 } 588 if (wlfound != NULL) 589 return (wlfound); 590 591 return (NULL); 592 } 593 594 /* 595 * Find a window index - if the window doesn't exist, check if it is a 596 * potential index and return it anyway. 597 */ 598 int 599 cmd_lookup_index(struct session *s, const char *name, int *ambiguous) 600 { 601 struct winlink *wl; 602 const char *errstr; 603 u_int idx; 604 605 if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL) 606 return (wl->idx); 607 if (*ambiguous) 608 return (-1); 609 610 idx = strtonum(name, 0, INT_MAX, &errstr); 611 if (errstr == NULL) 612 return (idx); 613 614 return (-1); 615 } 616 617 /* Find the target session or report an error and return NULL. */ 618 struct session * 619 cmd_find_session(struct cmd_ctx *ctx, const char *arg) 620 { 621 struct session *s; 622 struct client *c; 623 char *tmparg; 624 size_t arglen; 625 int ambiguous; 626 627 /* A NULL argument means the current session. */ 628 if (arg == NULL) 629 return (cmd_current_session(ctx)); 630 tmparg = xstrdup(arg); 631 632 /* Trim a single trailing colon if any. */ 633 arglen = strlen(tmparg); 634 if (arglen != 0 && tmparg[arglen - 1] == ':') 635 tmparg[arglen - 1] = '\0'; 636 637 /* Find the session, if any. */ 638 s = cmd_lookup_session(tmparg, &ambiguous); 639 640 /* If it doesn't, try to match it as a client. */ 641 if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL) 642 s = c->session; 643 644 /* If no session found, report an error. */ 645 if (s == NULL) { 646 if (ambiguous) 647 ctx->error(ctx, "more than one session: %s", tmparg); 648 else 649 ctx->error(ctx, "session not found: %s", tmparg); 650 } 651 652 xfree(tmparg); 653 return (s); 654 } 655 656 /* Find the target session and window or report an error and return NULL. */ 657 struct winlink * 658 cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp) 659 { 660 struct session *s; 661 struct winlink *wl; 662 const char *winptr; 663 char *sessptr = NULL; 664 int ambiguous = 0; 665 666 /* 667 * Find the current session. There must always be a current session, if 668 * it can't be found, report an error. 669 */ 670 if ((s = cmd_current_session(ctx)) == NULL) { 671 ctx->error(ctx, "can't establish current session"); 672 return (NULL); 673 } 674 675 /* A NULL argument means the current session and window. */ 676 if (arg == NULL) { 677 if (sp != NULL) 678 *sp = s; 679 return (s->curw); 680 } 681 682 /* Time to look at the argument. If it is empty, that is an error. */ 683 if (*arg == '\0') 684 goto not_found; 685 686 /* Find the separating colon and split into window and session. */ 687 winptr = strchr(arg, ':'); 688 if (winptr == NULL) 689 goto no_colon; 690 winptr++; /* skip : */ 691 sessptr = xstrdup(arg); 692 *strchr(sessptr, ':') = '\0'; 693 694 /* Try to lookup the session if present. */ 695 if (*sessptr != '\0') { 696 if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL) 697 goto no_session; 698 } 699 if (sp != NULL) 700 *sp = s; 701 702 /* 703 * Then work out the window. An empty string is the current window, 704 * otherwise try to look it up in the session. 705 */ 706 if (*winptr == '\0') 707 wl = s->curw; 708 else if ((wl = cmd_lookup_window(s, winptr, &ambiguous)) == NULL) 709 goto not_found; 710 711 if (sessptr != NULL) 712 xfree(sessptr); 713 return (wl); 714 715 no_colon: 716 /* No colon in the string, first try as a window then as a session. */ 717 if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL) { 718 if (ambiguous) 719 goto not_found; 720 if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL) 721 goto no_session; 722 wl = s->curw; 723 } 724 725 if (sp != NULL) 726 *sp = s; 727 728 return (wl); 729 730 no_session: 731 if (ambiguous) 732 ctx->error(ctx, "multiple sessions: %s", arg); 733 else 734 ctx->error(ctx, "session not found: %s", arg); 735 if (sessptr != NULL) 736 xfree(sessptr); 737 return (NULL); 738 739 not_found: 740 if (ambiguous) 741 ctx->error(ctx, "multiple windows: %s", arg); 742 else 743 ctx->error(ctx, "window not found: %s", arg); 744 if (sessptr != NULL) 745 xfree(sessptr); 746 return (NULL); 747 } 748 749 /* 750 * Find the target session and window index, whether or not it exists in the 751 * session. Return -2 on error or -1 if no window index is specified. This is 752 * used when parsing an argument for a window target that may not exist (for 753 * example if it is going to be created). 754 */ 755 int 756 cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp) 757 { 758 struct session *s; 759 const char *winptr; 760 char *sessptr = NULL; 761 int idx, ambiguous = 0; 762 763 /* 764 * Find the current session. There must always be a current session, if 765 * it can't be found, report an error. 766 */ 767 if ((s = cmd_current_session(ctx)) == NULL) { 768 ctx->error(ctx, "can't establish current session"); 769 return (-2); 770 } 771 772 /* A NULL argument means the current session and "no window" (-1). */ 773 if (arg == NULL) { 774 if (sp != NULL) 775 *sp = s; 776 return (-1); 777 } 778 779 /* Time to look at the argument. If it is empty, that is an error. */ 780 if (*arg == '\0') 781 goto not_found; 782 783 /* Find the separating colon. If none, assume the current session. */ 784 winptr = strchr(arg, ':'); 785 if (winptr == NULL) 786 goto no_colon; 787 winptr++; /* skip : */ 788 sessptr = xstrdup(arg); 789 *strchr(sessptr, ':') = '\0'; 790 791 /* Try to lookup the session if present. */ 792 if (sessptr != NULL && *sessptr != '\0') { 793 if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL) 794 goto no_session; 795 } 796 if (sp != NULL) 797 *sp = s; 798 799 /* 800 * Then work out the window. An empty string is a new window otherwise 801 * try to look it up in the session. 802 */ 803 if (*winptr == '\0') 804 idx = -1; 805 else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1) { 806 if (ambiguous) 807 goto not_found; 808 ctx->error(ctx, "invalid index: %s", arg); 809 idx = -2; 810 } 811 812 if (sessptr != NULL) 813 xfree(sessptr); 814 return (idx); 815 816 no_colon: 817 /* No colon in the string, first try as a window then as a session. */ 818 if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1) { 819 if (ambiguous) 820 goto not_found; 821 if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL) 822 goto no_session; 823 idx = -1; 824 } 825 826 if (sp != NULL) 827 *sp = s; 828 829 return (idx); 830 831 no_session: 832 if (ambiguous) 833 ctx->error(ctx, "multiple sessions: %s", arg); 834 else 835 ctx->error(ctx, "session not found: %s", arg); 836 if (sessptr != NULL) 837 xfree(sessptr); 838 return (-2); 839 840 not_found: 841 if (ambiguous) 842 ctx->error(ctx, "multiple windows: %s", arg); 843 else 844 ctx->error(ctx, "window not found: %s", arg); 845 if (sessptr != NULL) 846 xfree(sessptr); 847 return (-2); 848 } 849 850 /* 851 * Find the target session, window and pane number or report an error and 852 * return NULL. The pane number is separated from the session:window by a ., 853 * such as mysession:mywindow.0. 854 */ 855 struct winlink * 856 cmd_find_pane(struct cmd_ctx *ctx, 857 const char *arg, struct session **sp, struct window_pane **wpp) 858 { 859 struct session *s; 860 struct winlink *wl; 861 struct layout_cell *lc; 862 const char *period, *errstr; 863 char *winptr, *paneptr; 864 u_int idx; 865 866 /* Get the current session. */ 867 if ((s = cmd_current_session(ctx)) == NULL) { 868 ctx->error(ctx, "can't establish current session"); 869 return (NULL); 870 } 871 if (sp != NULL) 872 *sp = s; 873 874 /* A NULL argument means the current session, window and pane. */ 875 if (arg == NULL) { 876 *wpp = s->curw->window->active; 877 return (s->curw); 878 } 879 880 /* Look for a separating period. */ 881 if ((period = strrchr(arg, '.')) == NULL) 882 goto no_period; 883 884 /* Pull out the window part and parse it. */ 885 winptr = xstrdup(arg); 886 winptr[period - arg] = '\0'; 887 if (*winptr == '\0') 888 wl = s->curw; 889 else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL) 890 goto error; 891 892 /* Find the pane section and look it up. */ 893 paneptr = winptr + (period - arg) + 1; 894 if (*paneptr == '\0') 895 *wpp = wl->window->active; 896 else { 897 idx = strtonum(paneptr, 0, INT_MAX, &errstr); 898 if (errstr != NULL) 899 goto lookup_string; 900 *wpp = window_pane_at_index(wl->window, idx); 901 if (*wpp == NULL) 902 goto lookup_string; 903 } 904 905 xfree(winptr); 906 return (wl); 907 908 lookup_string: 909 /* Try pane string description. */ 910 if ((lc = layout_find_string(s->curw->window, paneptr)) == NULL) { 911 ctx->error(ctx, "can't find pane: %s", paneptr); 912 goto error; 913 } 914 *wpp = lc->wp; 915 916 xfree(winptr); 917 return (s->curw); 918 919 no_period: 920 /* Try as a pane number alone. */ 921 idx = strtonum(arg, 0, INT_MAX, &errstr); 922 if (errstr != NULL) 923 goto lookup_window; 924 925 /* Try index in the current session and window. */ 926 if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL) 927 goto lookup_window; 928 929 return (s->curw); 930 931 lookup_window: 932 /* Try pane string description. */ 933 if ((lc = layout_find_string(s->curw->window, arg)) != NULL) { 934 *wpp = lc->wp; 935 return (s->curw); 936 } 937 938 /* Try as a window and use the active pane. */ 939 if ((wl = cmd_find_window(ctx, arg, sp)) != NULL) 940 *wpp = wl->window->active; 941 return (wl); 942 943 error: 944 xfree(winptr); 945 return (NULL); 946 } 947 948 /* Replace the first %% or %idx in template by s. */ 949 char * 950 cmd_template_replace(char *template, const char *s, int idx) 951 { 952 char ch; 953 char *buf, *ptr; 954 int replaced; 955 size_t len; 956 957 if (strstr(template, "%") == NULL) 958 return (xstrdup(template)); 959 960 buf = xmalloc(1); 961 *buf = '\0'; 962 len = 0; 963 replaced = 0; 964 965 ptr = template; 966 while (*ptr != '\0') { 967 switch (ch = *ptr++) { 968 case '%': 969 if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) { 970 if (*ptr != '%' || replaced) 971 break; 972 replaced = 1; 973 } 974 ptr++; 975 976 len += strlen(s); 977 buf = xrealloc(buf, 1, len + 1); 978 strlcat(buf, s, len + 1); 979 continue; 980 } 981 buf = xrealloc(buf, 1, len + 2); 982 buf[len++] = ch; 983 buf[len] = '\0'; 984 } 985 986 return (buf); 987 } 988