File: | src/usr.bin/tmux/spawn.c |
Warning: | line 183, column 8 Access to field 'name' results in a dereference of a null pointer (loaded from variable 'w') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: spawn.c,v 1.33 2023/07/10 09:24:53 nicm Exp $ */ | |||
2 | ||||
3 | /* | |||
4 | * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> | |||
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(*__errno()).h> | |||
22 | #include <paths.h> | |||
23 | #include <signal.h> | |||
24 | #include <stdlib.h> | |||
25 | #include <string.h> | |||
26 | #include <unistd.h> | |||
27 | #include <util.h> | |||
28 | ||||
29 | #include "tmux.h" | |||
30 | ||||
31 | /* | |||
32 | * Set up the environment and create a new window and pane or a new pane. | |||
33 | * | |||
34 | * We need to set up the following items: | |||
35 | * | |||
36 | * - history limit, comes from the session; | |||
37 | * | |||
38 | * - base index, comes from the session; | |||
39 | * | |||
40 | * - current working directory, may be specified - if it isn't it comes from | |||
41 | * either the client or the session; | |||
42 | * | |||
43 | * - PATH variable, comes from the client if any, otherwise from the session | |||
44 | * environment; | |||
45 | * | |||
46 | * - shell, comes from default-shell; | |||
47 | * | |||
48 | * - termios, comes from the session; | |||
49 | * | |||
50 | * - remaining environment, comes from the session. | |||
51 | */ | |||
52 | ||||
53 | static void | |||
54 | spawn_log(const char *from, struct spawn_context *sc) | |||
55 | { | |||
56 | struct session *s = sc->s; | |||
57 | struct winlink *wl = sc->wl; | |||
58 | struct window_pane *wp0 = sc->wp0; | |||
59 | const char *name = cmdq_get_name(sc->item); | |||
60 | char tmp[128]; | |||
61 | ||||
62 | log_debug("%s: %s, flags=%#x", from, name, sc->flags); | |||
63 | ||||
64 | if (wl != NULL((void *)0) && wp0 != NULL((void *)0)) | |||
65 | xsnprintf(tmp, sizeof tmp, "wl=%d wp0=%%%u", wl->idx, wp0->id); | |||
66 | else if (wl != NULL((void *)0)) | |||
67 | xsnprintf(tmp, sizeof tmp, "wl=%d wp0=none", wl->idx); | |||
68 | else if (wp0 != NULL((void *)0)) | |||
69 | xsnprintf(tmp, sizeof tmp, "wl=none wp0=%%%u", wp0->id); | |||
70 | else | |||
71 | xsnprintf(tmp, sizeof tmp, "wl=none wp0=none"); | |||
72 | log_debug("%s: s=$%u %s idx=%d", from, s->id, tmp, sc->idx); | |||
73 | log_debug("%s: name=%s", from, sc->name == NULL((void *)0) ? "none" : sc->name); | |||
74 | } | |||
75 | ||||
76 | struct winlink * | |||
77 | spawn_window(struct spawn_context *sc, char **cause) | |||
78 | { | |||
79 | struct cmdq_item *item = sc->item; | |||
80 | struct client *c = cmdq_get_client(item); | |||
81 | struct session *s = sc->s; | |||
82 | struct window *w; | |||
83 | struct window_pane *wp; | |||
84 | struct winlink *wl; | |||
85 | int idx = sc->idx; | |||
86 | u_int sx, sy, xpixel, ypixel; | |||
87 | ||||
88 | spawn_log(__func__, sc); | |||
89 | ||||
90 | /* | |||
91 | * If the window already exists, we are respawning, so destroy all the | |||
92 | * panes except one. | |||
93 | */ | |||
94 | if (sc->flags & SPAWN_RESPAWN0x4) { | |||
| ||||
95 | w = sc->wl->window; | |||
96 | if (~sc->flags & SPAWN_KILL0x1) { | |||
97 | TAILQ_FOREACH(wp, &w->panes, entry)for((wp) = ((&w->panes)->tqh_first); (wp) != ((void *)0); (wp) = ((wp)->entry.tqe_next)) { | |||
98 | if (wp->fd != -1) | |||
99 | break; | |||
100 | } | |||
101 | if (wp != NULL((void *)0)) { | |||
102 | xasprintf(cause, "window %s:%d still active", | |||
103 | s->name, sc->wl->idx); | |||
104 | return (NULL((void *)0)); | |||
105 | } | |||
106 | } | |||
107 | ||||
108 | sc->wp0 = TAILQ_FIRST(&w->panes)((&w->panes)->tqh_first); | |||
109 | TAILQ_REMOVE(&w->panes, sc->wp0, entry)do { if (((sc->wp0)->entry.tqe_next) != ((void *)0)) (sc ->wp0)->entry.tqe_next->entry.tqe_prev = (sc->wp0 )->entry.tqe_prev; else (&w->panes)->tqh_last = ( sc->wp0)->entry.tqe_prev; *(sc->wp0)->entry.tqe_prev = (sc->wp0)->entry.tqe_next; ; ; } while (0); | |||
110 | ||||
111 | layout_free(w); | |||
112 | window_destroy_panes(w); | |||
113 | ||||
114 | TAILQ_INSERT_HEAD(&w->panes, sc->wp0, entry)do { if (((sc->wp0)->entry.tqe_next = (&w->panes )->tqh_first) != ((void *)0)) (&w->panes)->tqh_first ->entry.tqe_prev = &(sc->wp0)->entry.tqe_next; else (&w->panes)->tqh_last = &(sc->wp0)->entry .tqe_next; (&w->panes)->tqh_first = (sc->wp0); ( sc->wp0)->entry.tqe_prev = &(&w->panes)-> tqh_first; } while (0); | |||
115 | window_pane_resize(sc->wp0, w->sx, w->sy); | |||
116 | ||||
117 | layout_init(w, sc->wp0); | |||
118 | w->active = NULL((void *)0); | |||
119 | window_set_active_pane(w, sc->wp0, 0); | |||
120 | } | |||
121 | ||||
122 | /* | |||
123 | * Otherwise we have no window so we will need to create one. First | |||
124 | * check if the given index already exists and destroy it if so. | |||
125 | */ | |||
126 | if ((~sc->flags & SPAWN_RESPAWN0x4) && idx != -1) { | |||
127 | wl = winlink_find_by_index(&s->windows, idx); | |||
128 | if (wl != NULL((void *)0) && (~sc->flags & SPAWN_KILL0x1)) { | |||
129 | xasprintf(cause, "index %d in use", idx); | |||
130 | return (NULL((void *)0)); | |||
131 | } | |||
132 | if (wl != NULL((void *)0)) { | |||
133 | /* | |||
134 | * Can't use session_detach as it will destroy session | |||
135 | * if this makes it empty. | |||
136 | */ | |||
137 | wl->flags &= ~WINLINK_ALERTFLAGS(0x1|0x2|0x4); | |||
138 | notify_session_window("window-unlinked", s, wl->window); | |||
139 | winlink_stack_remove(&s->lastw, wl); | |||
140 | winlink_remove(&s->windows, wl); | |||
141 | ||||
142 | if (s->curw == wl) { | |||
143 | s->curw = NULL((void *)0); | |||
144 | sc->flags &= ~SPAWN_DETACHED0x2; | |||
145 | } | |||
146 | } | |||
147 | } | |||
148 | ||||
149 | /* Then create a window if needed. */ | |||
150 | if (~sc->flags & SPAWN_RESPAWN0x4) { | |||
151 | if (idx == -1) | |||
152 | idx = -1 - options_get_number(s->options, "base-index"); | |||
153 | if ((sc->wl = winlink_add(&s->windows, idx)) == NULL((void *)0)) { | |||
154 | xasprintf(cause, "couldn't add window %d", idx); | |||
155 | return (NULL((void *)0)); | |||
156 | } | |||
157 | default_window_size(sc->tc, s, NULL((void *)0), &sx, &sy, &xpixel, &ypixel, | |||
158 | -1); | |||
159 | if ((w = window_create(sx, sy, xpixel, ypixel)) == NULL((void *)0)) { | |||
160 | winlink_remove(&s->windows, sc->wl); | |||
161 | xasprintf(cause, "couldn't create window %d", idx); | |||
162 | return (NULL((void *)0)); | |||
163 | } | |||
164 | if (s->curw == NULL((void *)0)) | |||
165 | s->curw = sc->wl; | |||
166 | sc->wl->session = s; | |||
167 | w->latest = sc->tc; | |||
168 | winlink_set_window(sc->wl, w); | |||
169 | } else | |||
170 | w = NULL((void *)0); | |||
171 | sc->flags |= SPAWN_NONOTIFY0x10; | |||
172 | ||||
173 | /* Spawn the pane. */ | |||
174 | wp = spawn_pane(sc, cause); | |||
175 | if (wp == NULL((void *)0)) { | |||
176 | if (~sc->flags & SPAWN_RESPAWN0x4) | |||
177 | winlink_remove(&s->windows, sc->wl); | |||
178 | return (NULL((void *)0)); | |||
179 | } | |||
180 | ||||
181 | /* Set the name of the new window. */ | |||
182 | if (~sc->flags & SPAWN_RESPAWN0x4) { | |||
183 | free(w->name); | |||
| ||||
184 | if (sc->name != NULL((void *)0)) { | |||
185 | w->name = format_single(item, sc->name, c, s, NULL((void *)0), | |||
186 | NULL((void *)0)); | |||
187 | options_set_number(w->options, "automatic-rename", 0); | |||
188 | } else | |||
189 | w->name = default_window_name(w); | |||
190 | } | |||
191 | ||||
192 | /* Switch to the new window if required. */ | |||
193 | if (~sc->flags & SPAWN_DETACHED0x2) | |||
194 | session_select(s, sc->wl->idx); | |||
195 | ||||
196 | /* Fire notification if new window. */ | |||
197 | if (~sc->flags & SPAWN_RESPAWN0x4) | |||
198 | notify_session_window("window-linked", s, w); | |||
199 | ||||
200 | session_group_synchronize_from(s); | |||
201 | return (sc->wl); | |||
202 | } | |||
203 | ||||
204 | struct window_pane * | |||
205 | spawn_pane(struct spawn_context *sc, char **cause) | |||
206 | { | |||
207 | struct cmdq_item *item = sc->item; | |||
208 | struct cmd_find_state *target = cmdq_get_target(item); | |||
209 | struct client *c = cmdq_get_client(item); | |||
210 | struct session *s = sc->s; | |||
211 | struct window *w = sc->wl->window; | |||
212 | struct window_pane *new_wp; | |||
213 | struct environ *child; | |||
214 | struct environ_entry *ee; | |||
215 | char **argv, *cp, **argvp, *argv0, *cwd, *new_cwd; | |||
216 | const char *cmd, *tmp; | |||
217 | int argc; | |||
218 | u_int idx; | |||
219 | struct termios now; | |||
220 | u_int hlimit; | |||
221 | struct winsize ws; | |||
222 | sigset_t set, oldset; | |||
223 | key_code key; | |||
224 | ||||
225 | spawn_log(__func__, sc); | |||
226 | ||||
227 | /* | |||
228 | * Work out the current working directory. If respawning, use | |||
229 | * the pane's stored one unless specified. | |||
230 | */ | |||
231 | if (sc->cwd != NULL((void *)0)) { | |||
232 | cwd = format_single(item, sc->cwd, c, target->s, NULL((void *)0), NULL((void *)0)); | |||
233 | if (*cwd != '/') { | |||
234 | xasprintf(&new_cwd, "%s/%s", server_client_get_cwd(c, | |||
235 | target->s), cwd); | |||
236 | free(cwd); | |||
237 | cwd = new_cwd; | |||
238 | } | |||
239 | } else if (~sc->flags & SPAWN_RESPAWN0x4) | |||
240 | cwd = xstrdup(server_client_get_cwd(c, target->s)); | |||
241 | else | |||
242 | cwd = NULL((void *)0); | |||
243 | ||||
244 | /* | |||
245 | * If we are respawning then get rid of the old process. Otherwise | |||
246 | * either create a new cell or assign to the one we are given. | |||
247 | */ | |||
248 | hlimit = options_get_number(s->options, "history-limit"); | |||
249 | if (sc->flags & SPAWN_RESPAWN0x4) { | |||
250 | if (sc->wp0->fd != -1 && (~sc->flags & SPAWN_KILL0x1)) { | |||
251 | window_pane_index(sc->wp0, &idx); | |||
252 | xasprintf(cause, "pane %s:%d.%u still active", | |||
253 | s->name, sc->wl->idx, idx); | |||
254 | free(cwd); | |||
255 | return (NULL((void *)0)); | |||
256 | } | |||
257 | if (sc->wp0->fd != -1) { | |||
258 | bufferevent_free(sc->wp0->event); | |||
259 | close(sc->wp0->fd); | |||
260 | } | |||
261 | window_pane_reset_mode_all(sc->wp0); | |||
262 | screen_reinit(&sc->wp0->base); | |||
263 | input_free(sc->wp0->ictx); | |||
264 | sc->wp0->ictx = NULL((void *)0); | |||
265 | new_wp = sc->wp0; | |||
266 | new_wp->flags &= ~(PANE_STATUSREADY0x200|PANE_STATUSDRAWN0x400); | |||
267 | } else if (sc->lc == NULL((void *)0)) { | |||
268 | new_wp = window_add_pane(w, NULL((void *)0), hlimit, sc->flags); | |||
269 | layout_init(w, new_wp); | |||
270 | } else { | |||
271 | new_wp = window_add_pane(w, sc->wp0, hlimit, sc->flags); | |||
272 | if (sc->flags & SPAWN_ZOOM0x80) | |||
273 | layout_assign_pane(sc->lc, new_wp, 1); | |||
274 | else | |||
275 | layout_assign_pane(sc->lc, new_wp, 0); | |||
276 | } | |||
277 | ||||
278 | /* | |||
279 | * Now we have a pane with nothing running in it ready for the new | |||
280 | * process. Work out the command and arguments and store the working | |||
281 | * directory. | |||
282 | */ | |||
283 | if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN0x4)) { | |||
284 | cmd = options_get_string(s->options, "default-command"); | |||
285 | if (cmd != NULL((void *)0) && *cmd != '\0') { | |||
286 | argc = 1; | |||
287 | argv = (char **)&cmd; | |||
288 | } else { | |||
289 | argc = 0; | |||
290 | argv = NULL((void *)0); | |||
291 | } | |||
292 | } else { | |||
293 | argc = sc->argc; | |||
294 | argv = sc->argv; | |||
295 | } | |||
296 | if (cwd != NULL((void *)0)) { | |||
297 | free(new_wp->cwd); | |||
298 | new_wp->cwd = cwd; | |||
299 | } | |||
300 | ||||
301 | /* | |||
302 | * Replace the stored arguments if there are new ones. If not, the | |||
303 | * existing ones will be used (they will only exist for respawn). | |||
304 | */ | |||
305 | if (argc > 0) { | |||
306 | cmd_free_argv(new_wp->argc, new_wp->argv); | |||
307 | new_wp->argc = argc; | |||
308 | new_wp->argv = cmd_copy_argv(argc, argv); | |||
309 | } | |||
310 | ||||
311 | /* Create an environment for this pane. */ | |||
312 | child = environ_for_session(s, 0); | |||
313 | if (sc->environ != NULL((void *)0)) | |||
314 | environ_copy(sc->environ, child); | |||
315 | environ_set(child, "TMUX_PANE", 0, "%%%u", new_wp->id); | |||
316 | ||||
317 | /* | |||
318 | * Then the PATH environment variable. The session one is replaced from | |||
319 | * the client if there is one because otherwise running "tmux new | |||
320 | * myprogram" wouldn't work if myprogram isn't in the session's path. | |||
321 | */ | |||
322 | if (c != NULL((void *)0) && c->session == NULL((void *)0)) { /* only unattached clients */ | |||
323 | ee = environ_find(c->environ, "PATH"); | |||
324 | if (ee != NULL((void *)0)) | |||
325 | environ_set(child, "PATH", 0, "%s", ee->value); | |||
326 | } | |||
327 | if (environ_find(child, "PATH") == NULL((void *)0)) | |||
328 | environ_set(child, "PATH", 0, "%s", _PATH_DEFPATH"/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin"); | |||
329 | ||||
330 | /* Then the shell. If respawning, use the old one. */ | |||
331 | if (~sc->flags & SPAWN_RESPAWN0x4) { | |||
332 | tmp = options_get_string(s->options, "default-shell"); | |||
333 | if (!checkshell(tmp)) | |||
334 | tmp = _PATH_BSHELL"/bin/sh"; | |||
335 | free(new_wp->shell); | |||
336 | new_wp->shell = xstrdup(tmp); | |||
337 | } | |||
338 | environ_set(child, "SHELL", 0, "%s", new_wp->shell); | |||
339 | ||||
340 | /* Log the arguments we are going to use. */ | |||
341 | log_debug("%s: shell=%s", __func__, new_wp->shell); | |||
342 | if (new_wp->argc != 0) { | |||
343 | cp = cmd_stringify_argv(new_wp->argc, new_wp->argv); | |||
344 | log_debug("%s: cmd=%s", __func__, cp); | |||
345 | free(cp); | |||
346 | } | |||
347 | log_debug("%s: cwd=%s", __func__, new_wp->cwd); | |||
348 | cmd_log_argv(new_wp->argc, new_wp->argv, "%s", __func__); | |||
349 | environ_log(child, "%s: environment ", __func__); | |||
350 | ||||
351 | /* Initialize the window size. */ | |||
352 | memset(&ws, 0, sizeof ws); | |||
353 | ws.ws_col = screen_size_x(&new_wp->base)((&new_wp->base)->grid->sx); | |||
354 | ws.ws_row = screen_size_y(&new_wp->base)((&new_wp->base)->grid->sy); | |||
355 | ws.ws_xpixel = w->xpixel * ws.ws_col; | |||
356 | ws.ws_ypixel = w->ypixel * ws.ws_row; | |||
357 | ||||
358 | /* Block signals until fork has completed. */ | |||
359 | sigfillset(&set); | |||
360 | sigprocmask(SIG_BLOCK1, &set, &oldset); | |||
361 | ||||
362 | /* If the command is empty, don't fork a child process. */ | |||
363 | if (sc->flags & SPAWN_EMPTY0x40) { | |||
364 | new_wp->flags |= PANE_EMPTY0x800; | |||
365 | new_wp->base.mode &= ~MODE_CURSOR0x1; | |||
366 | new_wp->base.mode |= MODE_CRLF0x4000; | |||
367 | goto complete; | |||
368 | } | |||
369 | ||||
370 | /* Fork the new process. */ | |||
371 | new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL((void *)0), &ws); | |||
372 | if (new_wp->pid == -1) { | |||
373 | xasprintf(cause, "fork failed: %s", strerror(errno(*__errno()))); | |||
374 | new_wp->fd = -1; | |||
375 | if (~sc->flags & SPAWN_RESPAWN0x4) { | |||
376 | server_client_remove_pane(new_wp); | |||
377 | layout_close_pane(new_wp); | |||
378 | window_remove_pane(w, new_wp); | |||
379 | } | |||
380 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
381 | environ_free(child); | |||
382 | return (NULL((void *)0)); | |||
383 | } | |||
384 | ||||
385 | /* In the parent process, everything is done now. */ | |||
386 | if (new_wp->pid != 0) | |||
387 | goto complete; | |||
388 | ||||
389 | /* | |||
390 | * Child process. Change to the working directory or home if that | |||
391 | * fails. | |||
392 | */ | |||
393 | if (chdir(new_wp->cwd) == 0) | |||
394 | environ_set(child, "PWD", 0, "%s", new_wp->cwd); | |||
395 | else if ((tmp = find_home()) != NULL((void *)0) && chdir(tmp) == 0) | |||
396 | environ_set(child, "PWD", 0, "%s", tmp); | |||
397 | else if (chdir("/") == 0) | |||
398 | environ_set(child, "PWD", 0, "/"); | |||
399 | else | |||
400 | fatal("chdir failed"); | |||
401 | ||||
402 | /* | |||
403 | * Update terminal escape characters from the session if available and | |||
404 | * force VERASE to tmux's backspace. | |||
405 | */ | |||
406 | if (tcgetattr(STDIN_FILENO0, &now) != 0) | |||
407 | _exit(1); | |||
408 | if (s->tio != NULL((void *)0)) | |||
409 | memcpy(now.c_cc, s->tio->c_cc, sizeof now.c_cc); | |||
410 | key = options_get_number(global_options, "backspace"); | |||
411 | if (key >= 0x7f) | |||
412 | now.c_cc[VERASE3] = '\177'; | |||
413 | else | |||
414 | now.c_cc[VERASE3] = key; | |||
415 | if (tcsetattr(STDIN_FILENO0, TCSANOW0, &now) != 0) | |||
416 | _exit(1); | |||
417 | ||||
418 | /* Clean up file descriptors and signals and update the environment. */ | |||
419 | proc_clear_signals(server_proc, 1); | |||
420 | closefrom(STDERR_FILENO2 + 1); | |||
421 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
422 | log_close(); | |||
423 | environ_push(child); | |||
424 | ||||
425 | /* | |||
426 | * If given multiple arguments, use execvp(). Copy the arguments to | |||
427 | * ensure they end in a NULL. | |||
428 | */ | |||
429 | if (new_wp->argc != 0 && new_wp->argc != 1) { | |||
430 | argvp = cmd_copy_argv(new_wp->argc, new_wp->argv); | |||
431 | execvp(argvp[0], argvp); | |||
432 | _exit(1); | |||
433 | } | |||
434 | ||||
435 | /* | |||
436 | * If one argument, pass it to $SHELL -c. Otherwise create a login | |||
437 | * shell. | |||
438 | */ | |||
439 | cp = strrchr(new_wp->shell, '/'); | |||
440 | if (new_wp->argc == 1) { | |||
441 | tmp = new_wp->argv[0]; | |||
442 | if (cp != NULL((void *)0) && cp[1] != '\0') | |||
443 | xasprintf(&argv0, "%s", cp + 1); | |||
444 | else | |||
445 | xasprintf(&argv0, "%s", new_wp->shell); | |||
446 | execl(new_wp->shell, argv0, "-c", tmp, (char *)NULL((void *)0)); | |||
447 | _exit(1); | |||
448 | } | |||
449 | if (cp != NULL((void *)0) && cp[1] != '\0') | |||
450 | xasprintf(&argv0, "-%s", cp + 1); | |||
451 | else | |||
452 | xasprintf(&argv0, "-%s", new_wp->shell); | |||
453 | execl(new_wp->shell, argv0, (char *)NULL((void *)0)); | |||
454 | _exit(1); | |||
455 | ||||
456 | complete: | |||
457 | new_wp->flags &= ~PANE_EXITED0x100; | |||
458 | ||||
459 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
460 | window_pane_set_event(new_wp); | |||
461 | ||||
462 | environ_free(child); | |||
463 | ||||
464 | if (sc->flags & SPAWN_RESPAWN0x4) | |||
465 | return (new_wp); | |||
466 | if ((~sc->flags & SPAWN_DETACHED0x2) || w->active == NULL((void *)0)) { | |||
467 | if (sc->flags & SPAWN_NONOTIFY0x10) | |||
468 | window_set_active_pane(w, new_wp, 0); | |||
469 | else | |||
470 | window_set_active_pane(w, new_wp, 1); | |||
471 | } | |||
472 | if (~sc->flags & SPAWN_NONOTIFY0x10) | |||
473 | notify_window("window-layout-changed", w); | |||
474 | return (new_wp); | |||
475 | } |