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