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