4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
27 * Create a new session and attach to the current terminal unless -d is given.
30 int cmd_new_session_parse(struct cmd
*, int, char **, char **);
31 int cmd_new_session_exec(struct cmd
*, struct cmd_ctx
*);
32 void cmd_new_session_free(struct cmd
*);
33 void cmd_new_session_init(struct cmd
*, int);
34 size_t cmd_new_session_print(struct cmd
*, char *, size_t);
36 struct cmd_new_session_data
{
44 const struct cmd_entry cmd_new_session_entry
= {
46 "[-d] [-n window-name] [-s session-name] [-t target-session] [command]",
47 CMD_STARTSERVER
|CMD_CANTNEST
|CMD_SENDENVIRON
, "",
49 cmd_new_session_parse
,
57 cmd_new_session_init(struct cmd
*self
, unused
int arg
)
59 struct cmd_new_session_data
*data
;
61 self
->data
= data
= xmalloc(sizeof *data
);
62 data
->flag_detached
= 0;
70 cmd_new_session_parse(struct cmd
*self
, int argc
, char **argv
, char **cause
)
72 struct cmd_new_session_data
*data
;
75 self
->entry
->init(self
, KEYC_NONE
);
78 while ((opt
= getopt(argc
, argv
, "ds:t:n:")) != -1) {
81 data
->flag_detached
= 1;
84 if (data
->newname
== NULL
)
85 data
->newname
= xstrdup(optarg
);
88 if (data
->target
== NULL
)
89 data
->target
= xstrdup(optarg
);
92 if (data
->winname
== NULL
)
93 data
->winname
= xstrdup(optarg
);
101 if (argc
!= 0 && argc
!= 1)
104 if (data
->target
!= NULL
&& (argc
== 1 || data
->winname
!= NULL
))
108 data
->cmd
= xstrdup(argv
[0]);
113 xasprintf(cause
, "usage: %s %s", self
->entry
->name
, self
->entry
->usage
);
115 self
->entry
->free(self
);
120 cmd_new_session_exec(struct cmd
*self
, struct cmd_ctx
*ctx
)
122 struct cmd_new_session_data
*data
= self
->data
;
123 struct session
*s
, *groupwith
;
125 struct window_pane
*wp
;
127 struct termios tio
, *tiop
;
129 char *overrides
, *cmd
, *cwd
, *cause
;
133 if (data
->newname
!= NULL
&& session_find(data
->newname
) != NULL
) {
134 ctx
->error(ctx
, "duplicate session: %s", data
->newname
);
139 if (data
->target
!= NULL
&&
140 (groupwith
= cmd_find_session(ctx
, data
->target
)) == NULL
)
144 * There are three cases:
146 * 1. If cmdclient is non-NULL, new-session has been called from the
147 * command-line - cmdclient is to become a new attached, interactive
148 * client. Unless -d is given, the terminal must be opened and then
149 * the client sent MSG_READY.
151 * 2. If cmdclient is NULL, new-session has been called from an
152 * existing client (such as a key binding).
154 * 3. Both are NULL, the command was in the configuration file. Treat
155 * this as if -d was given even if it was not.
157 * In all cases, a new additional session needs to be created and
158 * (unless -d) set as the current session for the client.
161 /* Set -d if no client. */
162 detached
= data
->flag_detached
;
163 if (ctx
->cmdclient
== NULL
&& ctx
->curclient
== NULL
)
167 * Save the termios settings, part of which is used for new windows in
170 * This is read again with tcgetattr() rather than using tty.tio as if
171 * detached, tty_open won't be called. Because of this, it must be done
172 * before opening the terminal as that calls tcsetattr() to prepare for
175 if (ctx
->cmdclient
!= NULL
&& ctx
->cmdclient
->tty
.fd
!= -1) {
176 if (tcgetattr(ctx
->cmdclient
->tty
.fd
, &tio
) != 0)
177 fatal("tcgetattr failed");
182 /* Open the terminal if necessary. */
183 if (!detached
&& ctx
->cmdclient
!= NULL
) {
184 if (!(ctx
->cmdclient
->flags
& CLIENT_TERMINAL
)) {
185 ctx
->error(ctx
, "not a terminal");
190 options_get_string(&global_s_options
, "terminal-overrides");
191 if (tty_open(&ctx
->cmdclient
->tty
, overrides
, &cause
) != 0) {
192 ctx
->error(ctx
, "open terminal failed: %s", cause
);
198 /* Get the new session working directory. */
199 if (ctx
->cmdclient
!= NULL
&& ctx
->cmdclient
->cwd
!= NULL
)
200 cwd
= ctx
->cmdclient
->cwd
;
202 cwd
= options_get_string(&global_s_options
, "default-path");
204 /* Find new session size. */
208 } else if (ctx
->cmdclient
!= NULL
) {
209 sx
= ctx
->cmdclient
->tty
.sx
;
210 sy
= ctx
->cmdclient
->tty
.sy
;
212 sx
= ctx
->curclient
->tty
.sx
;
213 sy
= ctx
->curclient
->tty
.sy
;
215 if (sy
> 0 && options_get_number(&global_s_options
, "status"))
222 /* Figure out the command for the new window. */
223 if (data
->target
!= NULL
)
225 else if (data
->cmd
!= NULL
)
228 cmd
= options_get_string(&global_s_options
, "default-command");
230 /* Construct the environment. */
232 update
= options_get_string(&global_s_options
, "update-environment");
233 if (ctx
->cmdclient
!= NULL
)
234 environ_update(update
, &ctx
->cmdclient
->environ
, &env
);
236 /* Create the new session. */
237 idx
= -1 - options_get_number(&global_s_options
, "base-index");
239 data
->newname
, cmd
, cwd
, &env
, tiop
, idx
, sx
, sy
, &cause
);
241 ctx
->error(ctx
, "create session failed: %s", cause
);
247 /* Set the initial window name if one given. */
248 if (cmd
!= NULL
&& data
->winname
!= NULL
) {
252 w
->name
= xstrdup(data
->winname
);
254 options_set_number(&w
->options
, "automatic-rename", 0);
258 * If a target session is given, this is to be part of a session group,
259 * so add it to the group and synchronize.
261 if (groupwith
!= NULL
) {
262 session_group_add(groupwith
, s
);
263 session_group_synchronize_to(s
);
264 session_select(s
, RB_ROOT(&s
->windows
)->idx
);
268 * Set the client to the new session. If a command client exists, it is
269 * taking this session and needs to get MSG_READY and stay around.
272 if (ctx
->cmdclient
!= NULL
) {
273 server_write_client(ctx
->cmdclient
, MSG_READY
, NULL
, 0);
274 ctx
->cmdclient
->session
= s
;
275 server_redraw_client(ctx
->cmdclient
);
277 ctx
->curclient
->session
= s
;
278 server_redraw_client(ctx
->curclient
);
282 server_update_socket();
285 * If there are still configuration file errors to display, put the new
286 * session's current window into more mode and display them now.
288 if (cfg_finished
&& !ARRAY_EMPTY(&cfg_causes
)) {
289 wp
= s
->curw
->window
->active
;
290 window_pane_set_mode(wp
, &window_copy_mode
);
291 window_copy_init_for_output(wp
);
292 for (i
= 0; i
< ARRAY_LENGTH(&cfg_causes
); i
++) {
293 cause
= ARRAY_ITEM(&cfg_causes
, i
);
294 window_copy_add(wp
, "%s", cause
);
297 ARRAY_FREE(&cfg_causes
);
300 return (!detached
); /* 1 means don't tell command client to exit */
304 cmd_new_session_free(struct cmd
*self
)
306 struct cmd_new_session_data
*data
= self
->data
;
308 if (data
->newname
!= NULL
)
309 xfree(data
->newname
);
310 if (data
->winname
!= NULL
)
311 xfree(data
->winname
);
312 if (data
->cmd
!= NULL
)
318 cmd_new_session_print(struct cmd
*self
, char *buf
, size_t len
)
320 struct cmd_new_session_data
*data
= self
->data
;
323 off
+= xsnprintf(buf
, len
, "%s", self
->entry
->name
);
326 if (off
< len
&& data
->flag_detached
)
327 off
+= xsnprintf(buf
+ off
, len
- off
, " -d");
328 if (off
< len
&& data
->winname
!= NULL
)
329 off
+= cmd_prarg(buf
+ off
, len
- off
, " -n ", data
->winname
);
330 if (off
< len
&& data
->newname
!= NULL
)
331 off
+= cmd_prarg(buf
+ off
, len
- off
, " -s ", data
->newname
);
332 if (off
< len
&& data
->target
!= NULL
)
333 off
+= cmd_prarg(buf
+ off
, len
- off
, " -t ", data
->target
);
334 if (off
< len
&& data
->cmd
!= NULL
)
335 off
+= cmd_prarg(buf
+ off
, len
- off
, " ", data
->cmd
);