Annotation of src/usr.bin/tmux/cmd.c, Revision 1.48
1.48 ! nicm 1: /* $OpenBSD: cmd.c,v 1.47 2010/12/21 22:37:59 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.43 nicm 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 *);
1.5 nicm 124:
1.10 nicm 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);
1.46 nicm 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);
1.10 nicm 183: }
184:
185: void
186: cmd_free_argv(int argc, char **argv)
187: {
188: int i;
189:
190: if (argc == 0)
1.35 nicm 191: return;
1.10 nicm 192: for (i = 0; i < argc; i++) {
193: if (argv[i] != NULL)
194: xfree(argv[i]);
195: }
196: xfree(argv);
197: }
198:
1.1 nicm 199: struct cmd *
200: cmd_parse(int argc, char **argv, char **cause)
201: {
202: const struct cmd_entry **entryp, *entry;
1.27 deraadt 203: struct cmd *cmd;
1.1 nicm 204: char s[BUFSIZ];
1.3 nicm 205: int opt, ambiguous = 0;
1.1 nicm 206:
207: *cause = NULL;
1.2 nicm 208: if (argc == 0) {
209: xasprintf(cause, "no command");
1.1 nicm 210: return (NULL);
1.2 nicm 211: }
1.1 nicm 212:
213: entry = NULL;
214: for (entryp = cmd_table; *entryp != NULL; entryp++) {
215: if ((*entryp)->alias != NULL &&
216: strcmp((*entryp)->alias, argv[0]) == 0) {
1.3 nicm 217: ambiguous = 0;
1.1 nicm 218: entry = *entryp;
219: break;
220: }
221:
222: if (strncmp((*entryp)->name, argv[0], strlen(argv[0])) != 0)
223: continue;
224: if (entry != NULL)
1.3 nicm 225: ambiguous = 1;
1.1 nicm 226: entry = *entryp;
227:
228: /* Bail now if an exact match. */
229: if (strcmp(entry->name, argv[0]) == 0)
230: break;
231: }
1.3 nicm 232: if (ambiguous)
233: goto ambiguous;
1.1 nicm 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: {
1.33 nicm 301: if (cmd->entry->print == NULL)
1.1 nicm 302: return (xsnprintf(buf, len, "%s", cmd->entry->name));
303: return (cmd->entry->print(cmd, buf, len));
304: }
305:
1.5 nicm 306: /*
307: * Figure out the current session. Use: 1) the current session, if the command
1.31 nicm 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.
1.5 nicm 312: */
1.1 nicm 313: struct session *
314: cmd_current_session(struct cmd_ctx *ctx)
315: {
316: struct msg_command_data *data = ctx->msgdata;
1.11 nicm 317: struct client *c = ctx->cmdclient;
1.5 nicm 318: struct session *s;
1.47 nicm 319: struct sessionslist ss;
1.11 nicm 320: struct winlink *wl;
321: struct window_pane *wp;
322: int found;
1.1 nicm 323:
1.14 nicm 324: if (ctx->curclient != NULL && ctx->curclient->session != NULL)
325: return (ctx->curclient->session);
1.1 nicm 326:
1.11 nicm 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);
1.47 nicm 334: RB_FOREACH(s, sessions, &sessions) {
1.11 nicm 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:
1.47 nicm 350: s = cmd_choose_session_list(&ss);
1.11 nicm 351: ARRAY_FREE(&ss);
352: if (s != NULL)
353: return (s);
354: }
355:
356: /* Use the session from the TMUX environment variable. */
1.47 nicm 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: }
1.1 nicm 380:
1.47 nicm 381: return (sbest);
1.5 nicm 382: }
383:
1.31 nicm 384: /* Find the most recently used session from a list. */
1.5 nicm 385: struct session *
1.47 nicm 386: cmd_choose_session_list(struct sessionslist *ss)
1.5 nicm 387: {
1.31 nicm 388: struct session *s, *sbest;
1.5 nicm 389: struct timeval *tv = NULL;
390: u_int i;
391:
1.31 nicm 392: sbest = NULL;
1.11 nicm 393: for (i = 0; i < ARRAY_LENGTH(ss); i++) {
394: if ((s = ARRAY_ITEM(ss, i)) == NULL)
1.5 nicm 395: continue;
396:
1.31 nicm 397: if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
398: sbest = s;
399: tv = &s->activity_time;
1.1 nicm 400: }
401: }
1.5 nicm 402:
1.31 nicm 403: return (sbest);
1.1 nicm 404: }
405:
1.30 nicm 406: /*
407: * Find the current client. First try the current client if set, then pick the
1.31 nicm 408: * most recently used of the clients attached to the current session if any,
409: * then of all clients.
1.30 nicm 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:
1.31 nicm 436: c = cmd_choose_client(&cc);
1.30 nicm 437: ARRAY_FREE(&cc);
438: if (c != NULL)
439: return (c);
440: }
441:
1.31 nicm 442: return (cmd_choose_client(&clients));
1.30 nicm 443: }
444:
1.31 nicm 445: /* Choose the most recently used client from a list. */
1.20 nicm 446: struct client *
1.31 nicm 447: cmd_choose_client(struct clients *cc)
1.20 nicm 448: {
1.31 nicm 449: struct client *c, *cbest;
1.20 nicm 450: struct timeval *tv = NULL;
451: u_int i;
452:
1.31 nicm 453: cbest = NULL;
1.30 nicm 454: for (i = 0; i < ARRAY_LENGTH(cc); i++) {
455: if ((c = ARRAY_ITEM(cc, i)) == NULL)
1.20 nicm 456: continue;
457: if (c->session == NULL)
458: continue;
459:
1.31 nicm 460: if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
461: cbest = c;
462: tv = &c->activity_time;
1.20 nicm 463: }
464: }
465:
1.31 nicm 466: return (cbest);
1.20 nicm 467: }
468:
1.5 nicm 469: /* Find the target client or report an error and return NULL. */
1.1 nicm 470: struct client *
471: cmd_find_client(struct cmd_ctx *ctx, const char *arg)
472: {
1.30 nicm 473: struct client *c;
1.5 nicm 474: char *tmparg;
475: size_t arglen;
1.1 nicm 476:
1.5 nicm 477: /* A NULL argument means the current client. */
1.30 nicm 478: if (arg == NULL)
479: return (cmd_current_client(ctx));
1.5 nicm 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++) {
1.25 nicm 510: c = ARRAY_ITEM(&clients, i);
511: if (c == NULL || c->session == NULL)
1.5 nicm 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: /*
1.28 nicm 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.
1.5 nicm 541: */
1.47 nicm 542: if ((s = session_find(name)) != NULL)
543: return (s);
1.28 nicm 544:
545: /*
546: * Otherwise look for partial matches, returning early if it is found to
547: * be ambiguous.
548: */
549: sfound = NULL;
1.47 nicm 550: RB_FOREACH(s, sessions, &sessions) {
1.5 nicm 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: }
1.35 nicm 560: return (sfound);
1.5 nicm 561: }
562:
563: /*
1.8 nicm 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
1.41 nicm 566: * idx if the window index is a valid number but there is no window with that
1.8 nicm 567: * index.
1.5 nicm 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: }
1.35 nicm 584:
1.5 nicm 585: /* Look for exact matches, error if more than one. */
586: wlfound = NULL;
587: RB_FOREACH(wl, winlinks, &s->windows) {
1.8 nicm 588: if (strcmp(name, wl->window->name) == 0) {
1.5 nicm 589: if (wlfound != NULL) {
590: *ambiguous = 1;
591: return (NULL);
592: }
593: wlfound = wl;
1.1 nicm 594: }
595: }
1.5 nicm 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) {
1.8 nicm 602: if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
603: fnmatch(name, wl->window->name, 0) == 0) {
1.5 nicm 604: if (wlfound != NULL) {
605: *ambiguous = 1;
606: return (NULL);
607: }
608: wlfound = wl;
609: }
1.35 nicm 610: }
1.5 nicm 611: if (wlfound != NULL)
612: return (wlfound);
613:
614: return (NULL);
1.1 nicm 615: }
616:
1.8 nicm 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:
1.5 nicm 640: /* Find the target session or report an error and return NULL. */
1.1 nicm 641: struct session *
642: cmd_find_session(struct cmd_ctx *ctx, const char *arg)
643: {
644: struct session *s;
1.5 nicm 645: struct client *c;
646: char *tmparg;
647: size_t arglen;
648: int ambiguous;
1.1 nicm 649:
1.5 nicm 650: /* A NULL argument means the current session. */
1.1 nicm 651: if (arg == NULL)
1.5 nicm 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);
1.1 nicm 673: }
1.5 nicm 674:
675: xfree(tmparg);
1.1 nicm 676: return (s);
677: }
678:
1.5 nicm 679: /* Find the target session and window or report an error and return NULL. */
1.1 nicm 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;
1.5 nicm 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:
1.8 nicm 709: /* Find the separating colon and split into window and session. */
1.5 nicm 710: winptr = strchr(arg, ':');
711: if (winptr == NULL)
1.8 nicm 712: goto no_colon;
713: winptr++; /* skip : */
714: sessptr = xstrdup(arg);
715: *strchr(sessptr, ':') = '\0';
1.5 nicm 716:
717: /* Try to lookup the session if present. */
1.8 nicm 718: if (*sessptr != '\0') {
719: if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
1.5 nicm 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,
1.38 nicm 727: * otherwise try special cases then to look it up in the session.
1.5 nicm 728: */
1.8 nicm 729: if (*winptr == '\0')
1.5 nicm 730: wl = s->curw;
1.38 nicm 731: else if (winptr[0] == '!' && winptr[1] == '\0')
732: wl = TAILQ_FIRST(&s->lastw);
1.43 nicm 733: else if (winptr[0] == '+' || winptr[0] == '-')
734: wl = cmd_find_window_offset(winptr, s, &ambiguous);
735: else
1.38 nicm 736: wl = cmd_lookup_window(s, winptr, &ambiguous);
737: if (wl == NULL)
1.5 nicm 738: goto not_found;
1.35 nicm 739:
1.5 nicm 740: if (sessptr != NULL)
741: xfree(sessptr);
742: return (wl);
743:
1.8 nicm 744: no_colon:
1.38 nicm 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;
1.41 nicm 752: } else if (arg[0] == '+' || arg[0] == '-') {
1.43 nicm 753: if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL)
1.41 nicm 754: goto lookup_session;
755: } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL)
756: goto lookup_session;
1.8 nicm 757:
758: if (sp != NULL)
759: *sp = s;
760:
761: return (wl);
762:
1.41 nicm 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:
1.5 nicm 774: no_session:
775: if (ambiguous)
1.8 nicm 776: ctx->error(ctx, "multiple sessions: %s", arg);
1.5 nicm 777: else
1.8 nicm 778: ctx->error(ctx, "session not found: %s", arg);
1.5 nicm 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: }
1.1 nicm 792:
1.43 nicm 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:
1.5 nicm 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
1.8 nicm 816: * used when parsing an argument for a window target that may not exist (for
817: * example if it is going to be created).
1.5 nicm 818: */
819: int
820: cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
821: {
822: struct session *s;
1.38 nicm 823: struct winlink *wl;
1.8 nicm 824: const char *winptr;
1.5 nicm 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");
1.9 nicm 834: return (-2);
1.1 nicm 835: }
1.5 nicm 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)
1.8 nicm 851: goto no_colon;
852: winptr++; /* skip : */
853: sessptr = xstrdup(arg);
854: *strchr(sessptr, ':') = '\0';
1.5 nicm 855:
856: /* Try to lookup the session if present. */
857: if (sessptr != NULL && *sessptr != '\0') {
1.8 nicm 858: if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
1.5 nicm 859: goto no_session;
860: }
1.1 nicm 861: if (sp != NULL)
862: *sp = s;
863:
1.5 nicm 864: /*
1.8 nicm 865: * Then work out the window. An empty string is a new window otherwise
866: * try to look it up in the session.
1.5 nicm 867: */
1.8 nicm 868: if (*winptr == '\0')
1.38 nicm 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;
1.41 nicm 874: } else if (winptr[0] == '+' || winptr[0] == '-') {
1.43 nicm 875: if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0)
1.41 nicm 876: goto invalid_index;
877: } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1)
878: goto invalid_index;
1.35 nicm 879:
1.5 nicm 880: if (sessptr != NULL)
881: xfree(sessptr);
882: return (idx);
883:
1.8 nicm 884: no_colon:
1.38 nicm 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;
1.41 nicm 893: } else if (arg[0] == '+' || arg[0] == '-') {
1.43 nicm 894: if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0)
1.41 nicm 895: goto lookup_session;
896: } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1)
897: goto lookup_session;
1.8 nicm 898:
899: if (sp != NULL)
900: *sp = s;
901:
902: return (idx);
903:
1.41 nicm 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:
1.5 nicm 915: no_session:
916: if (ambiguous)
1.35 nicm 917: ctx->error(ctx, "multiple sessions: %s", arg);
1.5 nicm 918: else
1.8 nicm 919: ctx->error(ctx, "session not found: %s", arg);
1.5 nicm 920: if (sessptr != NULL)
921: xfree(sessptr);
922: return (-2);
923:
1.41 nicm 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:
1.5 nicm 933: not_found:
934: if (ambiguous)
935: ctx->error(ctx, "multiple windows: %s", arg);
1.1 nicm 936: else
1.5 nicm 937: ctx->error(ctx, "window not found: %s", arg);
938: if (sessptr != NULL)
939: xfree(sessptr);
940: return (-2);
1.12 nicm 941: }
942:
1.43 nicm 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:
1.12 nicm 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: {
1.36 nicm 978: struct session *s;
979: struct winlink *wl;
980: struct layout_cell *lc;
981: const char *period, *errstr;
982: char *winptr, *paneptr;
1.43 nicm 983: u_int idx;
1.12 nicm 984:
985: /* Get the current session. */
986: if ((s = cmd_current_session(ctx)) == NULL) {
1.27 deraadt 987: ctx->error(ctx, "can't establish current session");
1.12 nicm 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;
1.43 nicm 1015: else if (paneptr[0] == '+' || paneptr[0] == '-')
1016: *wpp = cmd_find_pane_offset(paneptr, wl);
1017: else {
1.12 nicm 1018: idx = strtonum(paneptr, 0, INT_MAX, &errstr);
1.36 nicm 1019: if (errstr != NULL)
1020: goto lookup_string;
1.12 nicm 1021: *wpp = window_pane_at_index(wl->window, idx);
1.36 nicm 1022: if (*wpp == NULL)
1023: goto lookup_string;
1.12 nicm 1024: }
1025:
1026: xfree(winptr);
1027: return (wl);
1028:
1.36 nicm 1029: lookup_string:
1030: /* Try pane string description. */
1.39 nicm 1031: if ((lc = layout_find_string(wl->window, paneptr)) == NULL) {
1.36 nicm 1032: ctx->error(ctx, "can't find pane: %s", paneptr);
1033: goto error;
1034: }
1035: *wpp = lc->wp;
1036:
1037: xfree(winptr);
1.39 nicm 1038: return (wl);
1.36 nicm 1039:
1.12 nicm 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;
1.35 nicm 1049:
1.12 nicm 1050: return (s->curw);
1051:
1052: lookup_window:
1.36 nicm 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:
1.12 nicm 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);
1.35 nicm 1063:
1.12 nicm 1064: error:
1065: xfree(winptr);
1066: return (NULL);
1.43 nicm 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);
1.15 nicm 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);
1.1 nicm 1127: }