Annotation of src/usr.bin/tmux/cmd.c, Revision 1.46
1.46 ! nicm 1: /* $OpenBSD: cmd.c,v 1.45 2010/10/23 13:04:34 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_buffer_entry,
44: &cmd_copy_mode_entry,
45: &cmd_delete_buffer_entry,
46: &cmd_detach_client_entry,
1.7 nicm 47: &cmd_display_message_entry,
1.16 nicm 48: &cmd_display_panes_entry,
1.1 nicm 49: &cmd_find_window_entry,
50: &cmd_has_session_entry,
1.4 nicm 51: &cmd_if_shell_entry,
1.37 nicm 52: &cmd_join_pane_entry,
1.1 nicm 53: &cmd_kill_pane_entry,
54: &cmd_kill_server_entry,
55: &cmd_kill_session_entry,
56: &cmd_kill_window_entry,
1.45 nicm 57: &cmd_last_pane_entry,
1.1 nicm 58: &cmd_last_window_entry,
59: &cmd_link_window_entry,
60: &cmd_list_buffers_entry,
61: &cmd_list_clients_entry,
62: &cmd_list_commands_entry,
63: &cmd_list_keys_entry,
1.23 nicm 64: &cmd_list_panes_entry,
1.1 nicm 65: &cmd_list_sessions_entry,
66: &cmd_list_windows_entry,
67: &cmd_load_buffer_entry,
1.19 nicm 68: &cmd_lock_client_entry,
1.1 nicm 69: &cmd_lock_server_entry,
1.19 nicm 70: &cmd_lock_session_entry,
1.1 nicm 71: &cmd_move_window_entry,
72: &cmd_new_session_entry,
73: &cmd_new_window_entry,
74: &cmd_next_layout_entry,
75: &cmd_next_window_entry,
76: &cmd_paste_buffer_entry,
1.24 nicm 77: &cmd_pipe_pane_entry,
1.1 nicm 78: &cmd_previous_layout_entry,
79: &cmd_previous_window_entry,
80: &cmd_refresh_client_entry,
81: &cmd_rename_session_entry,
82: &cmd_rename_window_entry,
83: &cmd_resize_pane_entry,
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.31 nicm 115: struct session *cmd_choose_session(struct sessions *);
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.11 nicm 319: struct sessions ss;
320: struct winlink *wl;
321: struct window_pane *wp;
322: u_int i;
323: int found;
1.1 nicm 324:
1.14 nicm 325: if (ctx->curclient != NULL && ctx->curclient->session != NULL)
326: return (ctx->curclient->session);
1.1 nicm 327:
1.11 nicm 328: /*
329: * If the name of the calling client's pty is know, build a list of the
330: * sessions that contain it and if any choose either the first or the
331: * newest.
332: */
333: if (c != NULL && c->tty.path != NULL) {
334: ARRAY_INIT(&ss);
335: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
336: if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
337: continue;
338: found = 0;
339: RB_FOREACH(wl, winlinks, &s->windows) {
340: TAILQ_FOREACH(wp, &wl->window->panes, entry) {
341: if (strcmp(wp->tty, c->tty.path) == 0) {
342: found = 1;
343: break;
344: }
345: }
346: if (found)
347: break;
348: }
349: if (found)
350: ARRAY_ADD(&ss, s);
351: }
352:
1.31 nicm 353: s = cmd_choose_session(&ss);
1.11 nicm 354: ARRAY_FREE(&ss);
355: if (s != NULL)
356: return (s);
357: }
358:
359: /* Use the session from the TMUX environment variable. */
1.44 nicm 360: if (data != NULL &&
361: data->pid == getpid() &&
362: data->idx <= ARRAY_LENGTH(&sessions) &&
363: (s = ARRAY_ITEM(&sessions, data->idx)) != NULL)
1.1 nicm 364: return (s);
365:
1.31 nicm 366: return (cmd_choose_session(&sessions));
1.5 nicm 367: }
368:
1.31 nicm 369: /* Find the most recently used session from a list. */
1.5 nicm 370: struct session *
1.31 nicm 371: cmd_choose_session(struct sessions *ss)
1.5 nicm 372: {
1.31 nicm 373: struct session *s, *sbest;
1.5 nicm 374: struct timeval *tv = NULL;
375: u_int i;
376:
1.31 nicm 377: sbest = NULL;
1.11 nicm 378: for (i = 0; i < ARRAY_LENGTH(ss); i++) {
379: if ((s = ARRAY_ITEM(ss, i)) == NULL)
1.5 nicm 380: continue;
381:
1.31 nicm 382: if (tv == NULL || timercmp(&s->activity_time, tv, >)) {
383: sbest = s;
384: tv = &s->activity_time;
1.1 nicm 385: }
386: }
1.5 nicm 387:
1.31 nicm 388: return (sbest);
1.1 nicm 389: }
390:
1.30 nicm 391: /*
392: * Find the current client. First try the current client if set, then pick the
1.31 nicm 393: * most recently used of the clients attached to the current session if any,
394: * then of all clients.
1.30 nicm 395: */
396: struct client *
397: cmd_current_client(struct cmd_ctx *ctx)
398: {
399: struct session *s;
400: struct client *c;
401: struct clients cc;
402: u_int i;
403:
404: if (ctx->curclient != NULL)
405: return (ctx->curclient);
406:
407: /*
408: * No current client set. Find the current session and return the
409: * newest of its clients.
410: */
411: s = cmd_current_session(ctx);
412: if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
413: ARRAY_INIT(&cc);
414: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
415: if ((c = ARRAY_ITEM(&clients, i)) == NULL)
416: continue;
417: if (s == c->session)
418: ARRAY_ADD(&cc, c);
419: }
420:
1.31 nicm 421: c = cmd_choose_client(&cc);
1.30 nicm 422: ARRAY_FREE(&cc);
423: if (c != NULL)
424: return (c);
425: }
426:
1.31 nicm 427: return (cmd_choose_client(&clients));
1.30 nicm 428: }
429:
1.31 nicm 430: /* Choose the most recently used client from a list. */
1.20 nicm 431: struct client *
1.31 nicm 432: cmd_choose_client(struct clients *cc)
1.20 nicm 433: {
1.31 nicm 434: struct client *c, *cbest;
1.20 nicm 435: struct timeval *tv = NULL;
436: u_int i;
437:
1.31 nicm 438: cbest = NULL;
1.30 nicm 439: for (i = 0; i < ARRAY_LENGTH(cc); i++) {
440: if ((c = ARRAY_ITEM(cc, i)) == NULL)
1.20 nicm 441: continue;
442: if (c->session == NULL)
443: continue;
444:
1.31 nicm 445: if (tv == NULL || timercmp(&c->activity_time, tv, >)) {
446: cbest = c;
447: tv = &c->activity_time;
1.20 nicm 448: }
449: }
450:
1.31 nicm 451: return (cbest);
1.20 nicm 452: }
453:
1.5 nicm 454: /* Find the target client or report an error and return NULL. */
1.1 nicm 455: struct client *
456: cmd_find_client(struct cmd_ctx *ctx, const char *arg)
457: {
1.30 nicm 458: struct client *c;
1.5 nicm 459: char *tmparg;
460: size_t arglen;
1.1 nicm 461:
1.5 nicm 462: /* A NULL argument means the current client. */
1.30 nicm 463: if (arg == NULL)
464: return (cmd_current_client(ctx));
1.5 nicm 465: tmparg = xstrdup(arg);
466:
467: /* Trim a single trailing colon if any. */
468: arglen = strlen(tmparg);
469: if (arglen != 0 && tmparg[arglen - 1] == ':')
470: tmparg[arglen - 1] = '\0';
471:
472: /* Find the client, if any. */
473: c = cmd_lookup_client(tmparg);
474:
475: /* If no client found, report an error. */
476: if (c == NULL)
477: ctx->error(ctx, "client not found: %s", tmparg);
478:
479: xfree(tmparg);
480: return (c);
481: }
482:
483: /*
484: * Lookup a client by device path. Either of a full match and a match without a
485: * leading _PATH_DEV ("/dev/") is accepted.
486: */
487: struct client *
488: cmd_lookup_client(const char *name)
489: {
490: struct client *c;
491: const char *path;
492: u_int i;
493:
494: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
1.25 nicm 495: c = ARRAY_ITEM(&clients, i);
496: if (c == NULL || c->session == NULL)
1.5 nicm 497: continue;
498: path = c->tty.path;
499:
500: /* Check for exact matches. */
501: if (strcmp(name, path) == 0)
502: return (c);
503:
504: /* Check without leading /dev if present. */
505: if (strncmp(path, _PATH_DEV, (sizeof _PATH_DEV) - 1) != 0)
506: continue;
507: if (strcmp(name, path + (sizeof _PATH_DEV) - 1) == 0)
508: return (c);
509: }
510:
511: return (NULL);
512: }
513:
514: /* Lookup a session by name. If no session is found, NULL is returned. */
515: struct session *
516: cmd_lookup_session(const char *name, int *ambiguous)
517: {
518: struct session *s, *sfound;
519: u_int i;
520:
521: *ambiguous = 0;
522:
523: /*
1.28 nicm 524: * Look for matches. First look for exact matches - session names must
525: * be unique so an exact match can't be ambigious and can just be
526: * returned.
1.5 nicm 527: */
1.35 nicm 528: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1.5 nicm 529: if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
530: continue;
531: if (strcmp(name, s->name) == 0)
532: return (s);
1.28 nicm 533: }
534:
535: /*
536: * Otherwise look for partial matches, returning early if it is found to
537: * be ambiguous.
538: */
539: sfound = NULL;
1.35 nicm 540: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
1.28 nicm 541: if ((s = ARRAY_ITEM(&sessions, i)) == NULL)
542: continue;
1.5 nicm 543: if (strncmp(name, s->name, strlen(name)) == 0 ||
544: fnmatch(name, s->name, 0) == 0) {
545: if (sfound != NULL) {
546: *ambiguous = 1;
547: return (NULL);
548: }
549: sfound = s;
550: }
551: }
1.35 nicm 552: return (sfound);
1.5 nicm 553: }
554:
555: /*
1.8 nicm 556: * Lookup a window or return -1 if not found or ambigious. First try as an
557: * index and if invalid, use fnmatch or leading prefix. Return NULL but fill in
1.41 nicm 558: * idx if the window index is a valid number but there is no window with that
1.8 nicm 559: * index.
1.5 nicm 560: */
561: struct winlink *
562: cmd_lookup_window(struct session *s, const char *name, int *ambiguous)
563: {
564: struct winlink *wl, *wlfound;
565: const char *errstr;
566: u_int idx;
567:
568: *ambiguous = 0;
569:
570: /* First see if this is a valid window index in this session. */
571: idx = strtonum(name, 0, INT_MAX, &errstr);
572: if (errstr == NULL) {
573: if ((wl = winlink_find_by_index(&s->windows, idx)) != NULL)
574: return (wl);
575: }
1.35 nicm 576:
1.5 nicm 577: /* Look for exact matches, error if more than one. */
578: wlfound = NULL;
579: RB_FOREACH(wl, winlinks, &s->windows) {
1.8 nicm 580: if (strcmp(name, wl->window->name) == 0) {
1.5 nicm 581: if (wlfound != NULL) {
582: *ambiguous = 1;
583: return (NULL);
584: }
585: wlfound = wl;
1.1 nicm 586: }
587: }
1.5 nicm 588: if (wlfound != NULL)
589: return (wlfound);
590:
591: /* Now look for pattern matches, again error if multiple. */
592: wlfound = NULL;
593: RB_FOREACH(wl, winlinks, &s->windows) {
1.8 nicm 594: if (strncmp(name, wl->window->name, strlen(name)) == 0 ||
595: fnmatch(name, wl->window->name, 0) == 0) {
1.5 nicm 596: if (wlfound != NULL) {
597: *ambiguous = 1;
598: return (NULL);
599: }
600: wlfound = wl;
601: }
1.35 nicm 602: }
1.5 nicm 603: if (wlfound != NULL)
604: return (wlfound);
605:
606: return (NULL);
1.1 nicm 607: }
608:
1.8 nicm 609: /*
610: * Find a window index - if the window doesn't exist, check if it is a
611: * potential index and return it anyway.
612: */
613: int
614: cmd_lookup_index(struct session *s, const char *name, int *ambiguous)
615: {
616: struct winlink *wl;
617: const char *errstr;
618: u_int idx;
619:
620: if ((wl = cmd_lookup_window(s, name, ambiguous)) != NULL)
621: return (wl->idx);
622: if (*ambiguous)
623: return (-1);
624:
625: idx = strtonum(name, 0, INT_MAX, &errstr);
626: if (errstr == NULL)
627: return (idx);
628:
629: return (-1);
630: }
631:
1.5 nicm 632: /* Find the target session or report an error and return NULL. */
1.1 nicm 633: struct session *
634: cmd_find_session(struct cmd_ctx *ctx, const char *arg)
635: {
636: struct session *s;
1.5 nicm 637: struct client *c;
638: char *tmparg;
639: size_t arglen;
640: int ambiguous;
1.1 nicm 641:
1.5 nicm 642: /* A NULL argument means the current session. */
1.1 nicm 643: if (arg == NULL)
1.5 nicm 644: return (cmd_current_session(ctx));
645: tmparg = xstrdup(arg);
646:
647: /* Trim a single trailing colon if any. */
648: arglen = strlen(tmparg);
649: if (arglen != 0 && tmparg[arglen - 1] == ':')
650: tmparg[arglen - 1] = '\0';
651:
652: /* Find the session, if any. */
653: s = cmd_lookup_session(tmparg, &ambiguous);
654:
655: /* If it doesn't, try to match it as a client. */
656: if (s == NULL && (c = cmd_lookup_client(tmparg)) != NULL)
657: s = c->session;
658:
659: /* If no session found, report an error. */
660: if (s == NULL) {
661: if (ambiguous)
662: ctx->error(ctx, "more than one session: %s", tmparg);
663: else
664: ctx->error(ctx, "session not found: %s", tmparg);
1.1 nicm 665: }
1.5 nicm 666:
667: xfree(tmparg);
1.1 nicm 668: return (s);
669: }
670:
1.5 nicm 671: /* Find the target session and window or report an error and return NULL. */
1.1 nicm 672: struct winlink *
673: cmd_find_window(struct cmd_ctx *ctx, const char *arg, struct session **sp)
674: {
675: struct session *s;
676: struct winlink *wl;
1.5 nicm 677: const char *winptr;
678: char *sessptr = NULL;
679: int ambiguous = 0;
680:
681: /*
682: * Find the current session. There must always be a current session, if
683: * it can't be found, report an error.
684: */
685: if ((s = cmd_current_session(ctx)) == NULL) {
686: ctx->error(ctx, "can't establish current session");
687: return (NULL);
688: }
689:
690: /* A NULL argument means the current session and window. */
691: if (arg == NULL) {
692: if (sp != NULL)
693: *sp = s;
694: return (s->curw);
695: }
696:
697: /* Time to look at the argument. If it is empty, that is an error. */
698: if (*arg == '\0')
699: goto not_found;
700:
1.8 nicm 701: /* Find the separating colon and split into window and session. */
1.5 nicm 702: winptr = strchr(arg, ':');
703: if (winptr == NULL)
1.8 nicm 704: goto no_colon;
705: winptr++; /* skip : */
706: sessptr = xstrdup(arg);
707: *strchr(sessptr, ':') = '\0';
1.5 nicm 708:
709: /* Try to lookup the session if present. */
1.8 nicm 710: if (*sessptr != '\0') {
711: if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
1.5 nicm 712: goto no_session;
713: }
714: if (sp != NULL)
715: *sp = s;
716:
717: /*
718: * Then work out the window. An empty string is the current window,
1.38 nicm 719: * otherwise try special cases then to look it up in the session.
1.5 nicm 720: */
1.8 nicm 721: if (*winptr == '\0')
1.5 nicm 722: wl = s->curw;
1.38 nicm 723: else if (winptr[0] == '!' && winptr[1] == '\0')
724: wl = TAILQ_FIRST(&s->lastw);
1.43 nicm 725: else if (winptr[0] == '+' || winptr[0] == '-')
726: wl = cmd_find_window_offset(winptr, s, &ambiguous);
727: else
1.38 nicm 728: wl = cmd_lookup_window(s, winptr, &ambiguous);
729: if (wl == NULL)
1.5 nicm 730: goto not_found;
1.35 nicm 731:
1.5 nicm 732: if (sessptr != NULL)
733: xfree(sessptr);
734: return (wl);
735:
1.8 nicm 736: no_colon:
1.38 nicm 737: /*
738: * No colon in the string, first try special cases, then as a window
739: * and lastly as a session.
740: */
741: if (arg[0] == '!' && arg[1] == '\0') {
742: if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
743: goto not_found;
1.41 nicm 744: } else if (arg[0] == '+' || arg[0] == '-') {
1.43 nicm 745: if ((wl = cmd_find_window_offset(arg, s, &ambiguous)) == NULL)
1.41 nicm 746: goto lookup_session;
747: } else if ((wl = cmd_lookup_window(s, arg, &ambiguous)) == NULL)
748: goto lookup_session;
1.8 nicm 749:
750: if (sp != NULL)
751: *sp = s;
752:
753: return (wl);
754:
1.41 nicm 755: lookup_session:
756: if (ambiguous)
757: goto not_found;
758: if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
759: goto no_session;
760:
761: if (sp != NULL)
762: *sp = s;
763:
764: return (s->curw);
765:
1.5 nicm 766: no_session:
767: if (ambiguous)
1.8 nicm 768: ctx->error(ctx, "multiple sessions: %s", arg);
1.5 nicm 769: else
1.8 nicm 770: ctx->error(ctx, "session not found: %s", arg);
1.5 nicm 771: if (sessptr != NULL)
772: xfree(sessptr);
773: return (NULL);
774:
775: not_found:
776: if (ambiguous)
777: ctx->error(ctx, "multiple windows: %s", arg);
778: else
779: ctx->error(ctx, "window not found: %s", arg);
780: if (sessptr != NULL)
781: xfree(sessptr);
782: return (NULL);
783: }
1.1 nicm 784:
1.43 nicm 785: struct winlink *
786: cmd_find_window_offset(const char *winptr, struct session *s, int *ambiguous)
787: {
788: struct winlink *wl;
789: int offset = 1;
790:
791: if (winptr[1] != '\0')
792: offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
793: if (offset == 0)
794: wl = cmd_lookup_window(s, winptr, ambiguous);
795: else {
796: if (winptr[0] == '+')
797: wl = winlink_next_by_number(s->curw, s, offset);
798: else
799: wl = winlink_previous_by_number(s->curw, s, offset);
800: }
801:
802: return (wl);
803: }
804:
1.5 nicm 805: /*
806: * Find the target session and window index, whether or not it exists in the
807: * session. Return -2 on error or -1 if no window index is specified. This is
1.8 nicm 808: * used when parsing an argument for a window target that may not exist (for
809: * example if it is going to be created).
1.5 nicm 810: */
811: int
812: cmd_find_index(struct cmd_ctx *ctx, const char *arg, struct session **sp)
813: {
814: struct session *s;
1.38 nicm 815: struct winlink *wl;
1.8 nicm 816: const char *winptr;
1.5 nicm 817: char *sessptr = NULL;
818: int idx, ambiguous = 0;
819:
820: /*
821: * Find the current session. There must always be a current session, if
822: * it can't be found, report an error.
823: */
824: if ((s = cmd_current_session(ctx)) == NULL) {
825: ctx->error(ctx, "can't establish current session");
1.9 nicm 826: return (-2);
1.1 nicm 827: }
1.5 nicm 828:
829: /* A NULL argument means the current session and "no window" (-1). */
830: if (arg == NULL) {
831: if (sp != NULL)
832: *sp = s;
833: return (-1);
834: }
835:
836: /* Time to look at the argument. If it is empty, that is an error. */
837: if (*arg == '\0')
838: goto not_found;
839:
840: /* Find the separating colon. If none, assume the current session. */
841: winptr = strchr(arg, ':');
842: if (winptr == NULL)
1.8 nicm 843: goto no_colon;
844: winptr++; /* skip : */
845: sessptr = xstrdup(arg);
846: *strchr(sessptr, ':') = '\0';
1.5 nicm 847:
848: /* Try to lookup the session if present. */
849: if (sessptr != NULL && *sessptr != '\0') {
1.8 nicm 850: if ((s = cmd_lookup_session(sessptr, &ambiguous)) == NULL)
1.5 nicm 851: goto no_session;
852: }
1.1 nicm 853: if (sp != NULL)
854: *sp = s;
855:
1.5 nicm 856: /*
1.8 nicm 857: * Then work out the window. An empty string is a new window otherwise
858: * try to look it up in the session.
1.5 nicm 859: */
1.8 nicm 860: if (*winptr == '\0')
1.38 nicm 861: idx = -1;
862: else if (winptr[0] == '!' && winptr[1] == '\0') {
863: if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
864: goto not_found;
865: idx = wl->idx;
1.41 nicm 866: } else if (winptr[0] == '+' || winptr[0] == '-') {
1.43 nicm 867: if ((idx = cmd_find_index_offset(winptr, s, &ambiguous)) < 0)
1.41 nicm 868: goto invalid_index;
869: } else if ((idx = cmd_lookup_index(s, winptr, &ambiguous)) == -1)
870: goto invalid_index;
1.35 nicm 871:
1.5 nicm 872: if (sessptr != NULL)
873: xfree(sessptr);
874: return (idx);
875:
1.8 nicm 876: no_colon:
1.38 nicm 877: /*
878: * No colon in the string, first try special cases, then as a window
879: * and lastly as a session.
880: */
881: if (arg[0] == '!' && arg[1] == '\0') {
882: if ((wl = TAILQ_FIRST(&s->lastw)) == NULL)
883: goto not_found;
884: idx = wl->idx;
1.41 nicm 885: } else if (arg[0] == '+' || arg[0] == '-') {
1.43 nicm 886: if ((idx = cmd_find_index_offset(arg, s, &ambiguous)) < 0)
1.41 nicm 887: goto lookup_session;
888: } else if ((idx = cmd_lookup_index(s, arg, &ambiguous)) == -1)
889: goto lookup_session;
1.8 nicm 890:
891: if (sp != NULL)
892: *sp = s;
893:
894: return (idx);
895:
1.41 nicm 896: lookup_session:
897: if (ambiguous)
898: goto not_found;
899: if ((s = cmd_lookup_session(arg, &ambiguous)) == NULL)
900: goto no_session;
901:
902: if (sp != NULL)
903: *sp = s;
904:
905: return (-1);
906:
1.5 nicm 907: no_session:
908: if (ambiguous)
1.35 nicm 909: ctx->error(ctx, "multiple sessions: %s", arg);
1.5 nicm 910: else
1.8 nicm 911: ctx->error(ctx, "session not found: %s", arg);
1.5 nicm 912: if (sessptr != NULL)
913: xfree(sessptr);
914: return (-2);
915:
1.41 nicm 916: invalid_index:
917: if (ambiguous)
918: goto not_found;
919: ctx->error(ctx, "invalid index: %s", arg);
920:
921: if (sessptr != NULL)
922: xfree(sessptr);
923: return (-2);
924:
1.5 nicm 925: not_found:
926: if (ambiguous)
927: ctx->error(ctx, "multiple windows: %s", arg);
1.1 nicm 928: else
1.5 nicm 929: ctx->error(ctx, "window not found: %s", arg);
930: if (sessptr != NULL)
931: xfree(sessptr);
932: return (-2);
1.12 nicm 933: }
934:
1.43 nicm 935: int
936: cmd_find_index_offset(const char *winptr, struct session *s, int *ambiguous)
937: {
938: int idx, offset = 1;
939:
940: if (winptr[1] != '\0')
941: offset = strtonum(winptr + 1, 1, INT_MAX, NULL);
942: if (offset == 0)
943: idx = cmd_lookup_index(s, winptr, ambiguous);
944: else {
945: if (winptr[0] == '+') {
946: if (s->curw->idx == INT_MAX)
947: idx = cmd_lookup_index(s, winptr, ambiguous);
948: else
949: idx = s->curw->idx + offset;
950: } else {
951: if (s->curw->idx == 0)
952: idx = cmd_lookup_index(s, winptr, ambiguous);
953: else
954: idx = s->curw->idx - offset;
955: }
956: }
957:
958: return (idx);
959: }
960:
1.12 nicm 961: /*
962: * Find the target session, window and pane number or report an error and
963: * return NULL. The pane number is separated from the session:window by a .,
964: * such as mysession:mywindow.0.
965: */
966: struct winlink *
967: cmd_find_pane(struct cmd_ctx *ctx,
968: const char *arg, struct session **sp, struct window_pane **wpp)
969: {
1.36 nicm 970: struct session *s;
971: struct winlink *wl;
972: struct layout_cell *lc;
973: const char *period, *errstr;
974: char *winptr, *paneptr;
1.43 nicm 975: u_int idx;
1.12 nicm 976:
977: /* Get the current session. */
978: if ((s = cmd_current_session(ctx)) == NULL) {
1.27 deraadt 979: ctx->error(ctx, "can't establish current session");
1.12 nicm 980: return (NULL);
981: }
982: if (sp != NULL)
983: *sp = s;
984:
985: /* A NULL argument means the current session, window and pane. */
986: if (arg == NULL) {
987: *wpp = s->curw->window->active;
988: return (s->curw);
989: }
990:
991: /* Look for a separating period. */
992: if ((period = strrchr(arg, '.')) == NULL)
993: goto no_period;
994:
995: /* Pull out the window part and parse it. */
996: winptr = xstrdup(arg);
997: winptr[period - arg] = '\0';
998: if (*winptr == '\0')
999: wl = s->curw;
1000: else if ((wl = cmd_find_window(ctx, winptr, sp)) == NULL)
1001: goto error;
1002:
1003: /* Find the pane section and look it up. */
1004: paneptr = winptr + (period - arg) + 1;
1005: if (*paneptr == '\0')
1006: *wpp = wl->window->active;
1.43 nicm 1007: else if (paneptr[0] == '+' || paneptr[0] == '-')
1008: *wpp = cmd_find_pane_offset(paneptr, wl);
1009: else {
1.12 nicm 1010: idx = strtonum(paneptr, 0, INT_MAX, &errstr);
1.36 nicm 1011: if (errstr != NULL)
1012: goto lookup_string;
1.12 nicm 1013: *wpp = window_pane_at_index(wl->window, idx);
1.36 nicm 1014: if (*wpp == NULL)
1015: goto lookup_string;
1.12 nicm 1016: }
1017:
1018: xfree(winptr);
1019: return (wl);
1020:
1.36 nicm 1021: lookup_string:
1022: /* Try pane string description. */
1.39 nicm 1023: if ((lc = layout_find_string(wl->window, paneptr)) == NULL) {
1.36 nicm 1024: ctx->error(ctx, "can't find pane: %s", paneptr);
1025: goto error;
1026: }
1027: *wpp = lc->wp;
1028:
1029: xfree(winptr);
1.39 nicm 1030: return (wl);
1.36 nicm 1031:
1.12 nicm 1032: no_period:
1033: /* Try as a pane number alone. */
1034: idx = strtonum(arg, 0, INT_MAX, &errstr);
1035: if (errstr != NULL)
1036: goto lookup_window;
1037:
1038: /* Try index in the current session and window. */
1039: if ((*wpp = window_pane_at_index(s->curw->window, idx)) == NULL)
1040: goto lookup_window;
1.35 nicm 1041:
1.12 nicm 1042: return (s->curw);
1043:
1044: lookup_window:
1.36 nicm 1045: /* Try pane string description. */
1046: if ((lc = layout_find_string(s->curw->window, arg)) != NULL) {
1047: *wpp = lc->wp;
1048: return (s->curw);
1049: }
1050:
1.12 nicm 1051: /* Try as a window and use the active pane. */
1052: if ((wl = cmd_find_window(ctx, arg, sp)) != NULL)
1053: *wpp = wl->window->active;
1054: return (wl);
1.35 nicm 1055:
1.12 nicm 1056: error:
1057: xfree(winptr);
1058: return (NULL);
1.43 nicm 1059: }
1060:
1061: struct window_pane *
1062: cmd_find_pane_offset(const char *paneptr, struct winlink *wl)
1063: {
1064: struct window *w = wl->window;
1065: struct window_pane *wp = w->active;
1066: u_int offset = 1;
1067:
1068: if (paneptr[1] != '\0')
1069: offset = strtonum(paneptr + 1, 1, INT_MAX, NULL);
1070: if (offset > 0) {
1071: if (paneptr[0] == '+')
1072: wp = window_pane_next_by_number(w, wp, offset);
1073: else
1074: wp = window_pane_previous_by_number(w, wp, offset);
1075: }
1076:
1077: return (wp);
1.15 nicm 1078: }
1079:
1080: /* Replace the first %% or %idx in template by s. */
1081: char *
1082: cmd_template_replace(char *template, const char *s, int idx)
1083: {
1084: char ch;
1085: char *buf, *ptr;
1086: int replaced;
1087: size_t len;
1088:
1089: if (strstr(template, "%") == NULL)
1090: return (xstrdup(template));
1091:
1092: buf = xmalloc(1);
1093: *buf = '\0';
1094: len = 0;
1095: replaced = 0;
1096:
1097: ptr = template;
1098: while (*ptr != '\0') {
1099: switch (ch = *ptr++) {
1100: case '%':
1101: if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
1102: if (*ptr != '%' || replaced)
1103: break;
1104: replaced = 1;
1105: }
1106: ptr++;
1107:
1108: len += strlen(s);
1109: buf = xrealloc(buf, 1, len + 1);
1110: strlcat(buf, s, len + 1);
1111: continue;
1112: }
1113: buf = xrealloc(buf, 1, len + 2);
1114: buf[len++] = ch;
1115: buf[len] = '\0';
1116: }
1117:
1118: return (buf);
1.1 nicm 1119: }