xref: /openbsd/usr.bin/tmux/cmd-new-window.c (revision a6445c1d)
1 /* $OpenBSD: cmd-new-window.c,v 1.43 2014/10/20 23:35:28 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "tmux.h"
28 
29 /*
30  * Create a new window.
31  */
32 
33 #define NEW_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
34 
35 enum cmd_retval	cmd_new_window_exec(struct cmd *, struct cmd_q *);
36 
37 const struct cmd_entry cmd_new_window_entry = {
38 	"new-window", "neww",
39 	"ac:dF:kn:Pt:", 0, -1,
40 	"[-adkP] [-c start-directory] [-F format] [-n window-name] "
41 	CMD_TARGET_WINDOW_USAGE " [command]",
42 	0,
43 	cmd_new_window_exec
44 };
45 
46 enum cmd_retval
47 cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq)
48 {
49 	struct args		*args = self->args;
50 	struct session		*s;
51 	struct winlink		*wl;
52 	struct client		*c;
53 	const char		*cmd, *path, *template;
54 	char		       **argv, *cause, *cp;
55 	int			 argc, idx, last, detached, cwd, fd = -1;
56 	struct format_tree	*ft;
57 	struct environ_entry	*envent;
58 
59 	if (args_has(args, 'a')) {
60 		wl = cmd_find_window(cmdq, args_get(args, 't'), &s);
61 		if (wl == NULL)
62 			return (CMD_RETURN_ERROR);
63 		idx = wl->idx + 1;
64 
65 		/* Find the next free index. */
66 		for (last = idx; last < INT_MAX; last++) {
67 			if (winlink_find_by_index(&s->windows, last) == NULL)
68 				break;
69 		}
70 		if (last == INT_MAX) {
71 			cmdq_error(cmdq, "no free window indexes");
72 			return (CMD_RETURN_ERROR);
73 		}
74 
75 		/* Move everything from last - 1 to idx up a bit. */
76 		for (; last > idx; last--) {
77 			wl = winlink_find_by_index(&s->windows, last - 1);
78 			server_link_window(s, wl, s, last, 0, 0, NULL);
79 			server_unlink_window(s, wl);
80 		}
81 	} else {
82 		idx = cmd_find_index(cmdq, args_get(args, 't'), &s);
83 		if (idx == -2)
84 			return (CMD_RETURN_ERROR);
85 	}
86 	detached = args_has(args, 'd');
87 
88 	if (args->argc == 0) {
89 		cmd = options_get_string(&s->options, "default-command");
90 		if (cmd != NULL && *cmd != '\0') {
91 			argc = 1;
92 			argv = (char **)&cmd;
93 		} else {
94 			argc = 0;
95 			argv = NULL;
96 		}
97 	} else {
98 		argc = args->argc;
99 		argv = args->argv;
100 	}
101 
102 	path = NULL;
103 	if (cmdq->client != NULL && cmdq->client->session == NULL)
104 		envent = environ_find(&cmdq->client->environ, "PATH");
105 	else
106 		envent = environ_find(&s->environ, "PATH");
107 	if (envent != NULL)
108 		path = envent->value;
109 
110 	if (args_has(args, 'c')) {
111 		ft = format_create();
112 		if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL)
113 			format_client(ft, c);
114 		format_session(ft, s);
115 		format_winlink(ft, s, s->curw);
116 		format_window_pane(ft, s->curw->window->active);
117 		cp = format_expand(ft, args_get(args, 'c'));
118 		format_free(ft);
119 
120 		if (cp != NULL && *cp != '\0') {
121 			fd = open(cp, O_RDONLY|O_DIRECTORY);
122 			free(cp);
123 			if (fd == -1) {
124 				cmdq_error(cmdq, "bad working directory: %s",
125 				    strerror(errno));
126 				return (CMD_RETURN_ERROR);
127 			}
128 		} else if (cp != NULL)
129 			free(cp);
130 		cwd = fd;
131 	} else if (cmdq->client != NULL && cmdq->client->session == NULL)
132 		cwd = cmdq->client->cwd;
133 	else
134 		cwd = s->cwd;
135 
136 	wl = NULL;
137 	if (idx != -1)
138 		wl = winlink_find_by_index(&s->windows, idx);
139 	if (wl != NULL && args_has(args, 'k')) {
140 		/*
141 		 * Can't use session_detach as it will destroy session if this
142 		 * makes it empty.
143 		 */
144 		notify_window_unlinked(s, wl->window);
145 		wl->flags &= ~WINLINK_ALERTFLAGS;
146 		winlink_stack_remove(&s->lastw, wl);
147 		winlink_remove(&s->windows, wl);
148 
149 		/* Force select/redraw if current. */
150 		if (wl == s->curw) {
151 			detached = 0;
152 			s->curw = NULL;
153 		}
154 	}
155 
156 	if (idx == -1)
157 		idx = -1 - options_get_number(&s->options, "base-index");
158 	wl = session_new(s, args_get(args, 'n'), argc, argv, path, cwd, idx,
159 		&cause);
160 	if (wl == NULL) {
161 		cmdq_error(cmdq, "create window failed: %s", cause);
162 		free(cause);
163 		goto error;
164 	}
165 	if (!detached) {
166 		session_select(s, wl->idx);
167 		server_redraw_session_group(s);
168 	} else
169 		server_status_session_group(s);
170 
171 	if (args_has(args, 'P')) {
172 		if ((template = args_get(args, 'F')) == NULL)
173 			template = NEW_WINDOW_TEMPLATE;
174 
175 		ft = format_create();
176 		if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL)
177 			format_client(ft, c);
178 		format_session(ft, s);
179 		format_winlink(ft, s, wl);
180 		format_window_pane(ft, wl->window->active);
181 
182 		cp = format_expand(ft, template);
183 		cmdq_print(cmdq, "%s", cp);
184 		free(cp);
185 
186 		format_free(ft);
187 	}
188 
189 	if (fd != -1)
190 		close(fd);
191 	return (CMD_RETURN_NORMAL);
192 
193 error:
194 	if (fd != -1)
195 		close(fd);
196 	return (CMD_RETURN_ERROR);
197 }
198