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