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