diff --git a/client.c b/client.c
index b9e3b30a..e7fa4b71 100644
--- a/client.c
+++ b/client.c
@@ -48,13 +48,14 @@ enum {
 } client_exitreason = CLIENT_EXIT_NONE;
 int		client_exitval;
 enum msgtype	client_exittype;
+const char     *client_exitsession;
 int		client_attached;
 
 int		client_get_lock(char *);
 int		client_connect(char *, int);
 void		client_send_identify(int);
-void		client_send_environ(void);
-void		client_write_server(enum msgtype, void *, size_t);
+int		client_write_one(enum msgtype, int, const void *, size_t);
+int		client_write_server(enum msgtype, const void *, size_t);
 void		client_update_event(void);
 void		client_signal(int, short, void *);
 void		client_stdin_callback(int, short, void *);
@@ -138,12 +139,24 @@ failed:
 const char *
 client_exit_message(void)
 {
+	static char msg[256];
+
 	switch (client_exitreason) {
 	case CLIENT_EXIT_NONE:
 		break;
 	case CLIENT_EXIT_DETACHED:
+		if (client_exitsession != NULL) {
+			xsnprintf(msg, sizeof msg, "detached "
+			    "(from session %s)", client_exitsession);
+			return (msg);
+		}
 		return ("detached");
 	case CLIENT_EXIT_DETACHED_HUP:
+		if (client_exitsession != NULL) {
+			xsnprintf(msg, sizeof msg, "detached and SIGHUP "
+			    "(from session %s)", client_exitsession);
+			return (msg);
+		}
 		return ("detached and SIGHUP");
 	case CLIENT_EXIT_LOST_TTY:
 		return ("lost tty");
@@ -165,12 +178,13 @@ client_main(int argc, char **argv, int flags)
 {
 	struct cmd		*cmd;
 	struct cmd_list		*cmdlist;
-	struct msg_command_data	 cmddata;
-	int			 cmdflags, fd;
+	struct msg_command_data	*data;
+	int			 cmdflags, fd, i;
 	pid_t			 ppid;
 	enum msgtype		 msg;
 	char			*cause;
 	struct termios		 tio, saved_tio;
+	size_t			 size;
 
 	/* Set up the initial command. */
 	cmdflags = 0;
@@ -179,7 +193,7 @@ client_main(int argc, char **argv, int flags)
 		cmdflags = CMD_STARTSERVER;
 	} else if (argc == 0) {
 		msg = MSG_COMMAND;
-		cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST;
+		cmdflags = CMD_STARTSERVER|CMD_CANTNEST;
 	} else {
 		msg = MSG_COMMAND;
 
@@ -197,8 +211,6 @@ client_main(int argc, char **argv, int flags)
 		TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
 			if (cmd->entry->flags & CMD_STARTSERVER)
 				cmdflags |= CMD_STARTSERVER;
-			if (cmd->entry->flags & CMD_SENDENVIRON)
-				cmdflags |= CMD_SENDENVIRON;
 			if (cmd->entry->flags & CMD_CANTNEST)
 				cmdflags |= CMD_CANTNEST;
 		}
@@ -238,7 +250,7 @@ client_main(int argc, char **argv, int flags)
 	setblocking(STDIN_FILENO, 0);
 	event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
 	    client_stdin_callback, NULL);
-	if (flags & IDENTIFY_TERMIOS) {
+	if (flags & CLIENT_CONTROLCONTROL) {
 		if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
 			fprintf(stderr, "tcgetattr failed: %s\n",
 			    strerror(errno));
@@ -261,26 +273,33 @@ client_main(int argc, char **argv, int flags)
 	/* Establish signal handlers. */
 	set_signals(client_signal);
 
-	/* Send initial environment. */
-	if (cmdflags & CMD_SENDENVIRON)
-		client_send_environ();
+	/* Send identify messages. */
 	client_send_identify(flags);
 
 	/* Send first command. */
 	if (msg == MSG_COMMAND) {
-		/* Fill in command line arguments. */
-		cmddata.pid = environ_pid;
-		cmddata.session_id = environ_session_id;
+		/* How big is the command? */
+		size = 0;
+		for (i = 0; i < argc; i++)
+			size += strlen(argv[i]) + 1;
+		data = xmalloc((sizeof *data) + size);
 
 		/* Prepare command for server. */
-		cmddata.argc = argc;
-		if (cmd_pack_argv(
-		    argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
+		data->argc = argc;
+		if (cmd_pack_argv(argc, argv, (char*)(data + 1), size) != 0) {
 			fprintf(stderr, "command too long\n");
+			free(data);
 			return (1);
 		}
+		size += sizeof *data;
 
-		client_write_server(msg, &cmddata, sizeof cmddata);
+		/* Send the command. */
+		if (client_write_server(msg, data, size) != 0) {
+			fprintf(stderr, "failed to send command\n");
+			free(data);
+			return (1);
+		}
+		free(data);
 	} else if (msg == MSG_SHELL)
 		client_write_server(msg, NULL, 0);
 
@@ -296,37 +315,39 @@ client_main(int argc, char **argv, int flags)
 		ppid = getppid();
 		if (client_exittype == MSG_DETACHKILL && ppid > 1)
 			kill(ppid, SIGHUP);
-	} else if (flags & IDENTIFY_TERMIOS) {
-		if (flags & IDENTIFY_CONTROL) {
-			if (client_exitreason != CLIENT_EXIT_NONE)
-			    printf("%%exit %s\n", client_exit_message());
-			else
-			    printf("%%exit\n");
-			printf("\033\\");
-		}
+	} else if (flags & CLIENT_CONTROLCONTROL) {
+		if (client_exitreason != CLIENT_EXIT_NONE)
+			printf("%%exit %s\n", client_exit_message());
+		else
+			printf("%%exit\n");
+		printf("\033\\");
 		tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
 	}
 	setblocking(STDIN_FILENO, 1);
 	return (client_exitval);
 }
 
-/* Send identify message to server with the file descriptors. */
+/* Send identify messages to server. */
 void
 client_send_identify(int flags)
 {
-	struct msg_identify_data	data;
-	char			       *term;
-	int				fd;
+	const char	*s;
+	char		**ss;
+	int		 fd;
 
-	data.flags = flags;
+	client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
 
-	if (getcwd(data.cwd, sizeof data.cwd) == NULL)
-		*data.cwd = '\0';
+	if ((s = getenv("TERM")) == NULL)
+		s = "";
+	client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1);
 
-	term = getenv("TERM");
-	if (term == NULL ||
-	    strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
-		*data.term = '\0';
+	if ((s = ttyname(STDIN_FILENO)) == NULL)
+		s = "";
+	client_write_one(MSG_IDENTIFY_TTYNAME, -1, s, strlen(s) + 1);
+
+	if ((fd = open(".", O_RDONLY)) == -1)
+		fd = open("/", O_RDONLY);
+	client_write_one(MSG_IDENTIFY_CWD, fd, NULL, 0);
 
 #ifdef __CYGWIN__
 	snprintf(&data.ttyname, sizeof data.ttyname, "%s",
@@ -334,32 +355,39 @@ client_send_identify(int flags)
 #else
 	if ((fd = dup(STDIN_FILENO)) == -1)
 		fatal("dup failed");
-#endif
-	imsg_compose(&client_ibuf,
-	    MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
+	client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0);
+
+	for (ss = environ; *ss != NULL; ss++)
+		client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, strlen(*ss) + 1);
+
+	client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0);
+
 	client_update_event();
 }
 
-/* Forward entire environment to server. */
-void
-client_send_environ(void)
+/* Helper to send one message. */
+int
+client_write_one(enum msgtype type, int fd, const void *buf, size_t len)
 {
-	struct msg_environ_data	data;
-	char		      **var;
+	int	retval;
 
-	for (var = environ; *var != NULL; var++) {
-		if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
-			continue;
-		client_write_server(MSG_ENVIRON, &data, sizeof data);
-	}
+	retval = imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, fd,
+	    (void*)buf, len);
+	if (retval != 1)
+		return (-1);
+	return (0);
 }
 
 /* Write a message to the server without a file descriptor. */
-void
-client_write_server(enum msgtype type, void *buf, size_t len)
+int
+client_write_server(enum msgtype type, const void *buf, size_t len)
 {
-	imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
-	client_update_event();
+	int	retval;
+
+	retval = client_write_one(type, -1, buf, len);
+	if (retval == 0)
+		client_update_event();
+	return (retval);
 }
 
 /* Update client event based on whether it needs to read or read and write. */
@@ -493,33 +521,33 @@ client_write(int fd, const char *data, size_t size)
 
 /* Dispatch imsgs when in wait state (before MSG_READY). */
 int
-client_dispatch_wait(void *data)
+client_dispatch_wait(void *data0)
 {
-	struct imsg		imsg;
-	ssize_t			n, datalen;
-	struct msg_shell_data	shelldata;
-	struct msg_exit_data	exitdata;
-	struct msg_stdout_data	stdoutdata;
-	struct msg_stderr_data	stderrdata;
-	const char             *shellcmd = data;
+	struct imsg		 imsg;
+	char			*data;
+	ssize_t			 n, datalen;
+	struct msg_stdout_data	 stdoutdata;
+	struct msg_stderr_data	 stderrdata;
+	int			 retval;
 
 	for (;;) {
 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
 			fatalx("imsg_get failed");
 		if (n == 0)
 			return (0);
+
+		data = imsg.data;
 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
 
 		log_debug("got %d from server", imsg.hdr.type);
 		switch (imsg.hdr.type) {
 		case MSG_EXIT:
 		case MSG_SHUTDOWN:
-			if (datalen != sizeof exitdata) {
-				if (datalen != 0)
-					fatalx("bad MSG_EXIT size");
-			} else {
-				memcpy(&exitdata, imsg.data, sizeof exitdata);
-				client_exitval = exitdata.retcode;
+			if (datalen != sizeof retval && datalen != 0)
+				fatalx("bad MSG_EXIT size");
+			if (datalen == sizeof retval) {
+				memcpy(&retval, data, sizeof retval);
+				client_exitval = retval;
 			}
 			imsg_free(&imsg);
 			return (-1);
@@ -539,17 +567,19 @@ client_dispatch_wait(void *data)
 			break;
 		case MSG_STDOUT:
 			if (datalen != sizeof stdoutdata)
-				fatalx("bad MSG_STDOUT");
-			memcpy(&stdoutdata, imsg.data, sizeof stdoutdata);
+				fatalx("bad MSG_STDOUT size");
+			memcpy(&stdoutdata, data, sizeof stdoutdata);
 
-			client_write(STDOUT_FILENO, stdoutdata.data, stdoutdata.size);
+			client_write(STDOUT_FILENO, stdoutdata.data,
+			    stdoutdata.size);
 			break;
 		case MSG_STDERR:
 			if (datalen != sizeof stderrdata)
-				fatalx("bad MSG_STDERR");
-			memcpy(&stderrdata, imsg.data, sizeof stderrdata);
+				fatalx("bad MSG_STDERR size");
+			memcpy(&stderrdata, data, sizeof stderrdata);
 
-			client_write(STDERR_FILENO, stderrdata.data, stderrdata.size);
+			client_write(STDERR_FILENO, stderrdata.data,
+			    stderrdata.size);
 			break;
 		case MSG_VERSION:
 			if (datalen != 0)
@@ -563,23 +593,19 @@ client_dispatch_wait(void *data)
 			imsg_free(&imsg);
 			return (-1);
 		case MSG_SHELL:
-			if (datalen != sizeof shelldata)
-				fatalx("bad MSG_SHELL size");
-			memcpy(&shelldata, imsg.data, sizeof shelldata);
-			shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
+			if (datalen == 0 || data[datalen - 1] != '\0')
+				fatalx("bad MSG_SHELL string");
 
 			clear_signals(0);
-
-			shell_exec(shelldata.shell, shellcmd);
+			shell_exec(data, data0);
 			/* NOTREACHED */
 		case MSG_DETACH:
+		case MSG_DETACHKILL:
 			client_write_server(MSG_EXITING, NULL, 0);
 			break;
 		case MSG_EXITED:
 			imsg_free(&imsg);
 			return (-1);
-		default:
-			fatalx("unexpected message");
 		}
 
 		imsg_free(&imsg);
@@ -590,25 +616,28 @@ client_dispatch_wait(void *data)
 int
 client_dispatch_attached(void)
 {
-	struct imsg		imsg;
-	struct msg_lock_data	lockdata;
-	struct sigaction	sigact;
-	ssize_t			n, datalen;
+	struct imsg		 imsg;
+	struct sigaction	 sigact;
+	char			*data;
+	ssize_t			 n, datalen;
 
 	for (;;) {
 		if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
 			fatalx("imsg_get failed");
 		if (n == 0)
 			return (0);
+
+		data = imsg.data;
 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
 
 		log_debug("got %d from server", imsg.hdr.type);
 		switch (imsg.hdr.type) {
-		case MSG_DETACHKILL:
 		case MSG_DETACH:
-			if (datalen != 0)
-				fatalx("bad MSG_DETACH size");
+		case MSG_DETACHKILL:
+			if (datalen == 0 || data[datalen - 1] != '\0')
+				fatalx("bad MSG_DETACH string");
 
+			client_exitsession = xstrdup(data);
 			client_exittype = imsg.hdr.type;
 			if (imsg.hdr.type == MSG_DETACHKILL)
 				client_exitreason = CLIENT_EXIT_DETACHED_HUP;
@@ -617,8 +646,7 @@ client_dispatch_attached(void)
 			client_write_server(MSG_EXITING, NULL, 0);
 			break;
 		case MSG_EXIT:
-			if (datalen != 0 &&
-			    datalen != sizeof (struct msg_exit_data))
+			if (datalen != 0 && datalen != sizeof (int))
 				fatalx("bad MSG_EXIT size");
 
 			client_write_server(MSG_EXITING, NULL, 0);
@@ -651,16 +679,12 @@ client_dispatch_attached(void)
 			kill(getpid(), SIGTSTP);
 			break;
 		case MSG_LOCK:
-			if (datalen != sizeof lockdata)
-				fatalx("bad MSG_LOCK size");
-			memcpy(&lockdata, imsg.data, sizeof lockdata);
+			if (datalen == 0 || data[datalen - 1] != '\0')
+				fatalx("bad MSG_LOCK string");
 
-			lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
-			system(lockdata.cmd);
+			system(data);
 			client_write_server(MSG_UNLOCK, NULL, 0);
 			break;
-		default:
-			fatalx("unexpected message");
 		}
 
 		imsg_free(&imsg);
diff --git a/cmd-attach-session.c b/cmd-attach-session.c
index 07185737..e4c0b232 100644
--- a/cmd-attach-session.c
+++ b/cmd-attach-session.c
@@ -18,7 +18,11 @@
 
 #include <sys/types.h>
 
+#include <errno.h>
+#include <fcntl.h>
 #include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 #include "tmux.h"
 
@@ -30,22 +34,25 @@ enum cmd_retval	cmd_attach_session_exec(struct cmd *, struct cmd_q *);
 
 const struct cmd_entry cmd_attach_session_entry = {
 	"attach-session", "attach",
-	"drt:", 0, 0,
-	"[-dr] " CMD_TARGET_SESSION_USAGE,
-	CMD_CANTNEST|CMD_STARTSERVER|CMD_SENDENVIRON,
-	NULL,
+	"c:drt:", 0, 0,
+	"[-dr] [-c working-directory] " CMD_TARGET_SESSION_USAGE,
+	CMD_CANTNEST|CMD_STARTSERVER,
 	NULL,
 	cmd_attach_session_exec
 };
 
 enum cmd_retval
-cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag)
+cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag,
+    const char *cflag)
 {
-	struct session	*s;
-	struct client	*c;
-	const char	*update;
-	char		*cause;
-	u_int		 i;
+	struct session		*s;
+	struct client		*c;
+	const char		*update;
+	char			*cause;
+	u_int			 i;
+	int			 fd;
+	struct format_tree	*ft;
+	char			*cp;
 
 	if (RB_EMPTY(&sessions)) {
 		cmdq_error(cmdq, "no sessions");
@@ -70,10 +77,33 @@ cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag)
 					continue;
 				if (c == cmdq->client)
 					continue;
-				server_write_client(c, MSG_DETACH, NULL, 0);
+				server_write_client(c, MSG_DETACH,
+				    c->session->name,
+				    strlen(c->session->name) + 1);
 			}
 		}
 
+		if (cflag != NULL) {
+			ft = format_create();
+			if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL)
+				format_client(ft, c);
+			format_session(ft, s);
+			format_winlink(ft, s, s->curw);
+			format_window_pane(ft, s->curw->window->active);
+			cp = format_expand(ft, cflag);
+			format_free(ft);
+
+			fd = open(cp, O_RDONLY|O_DIRECTORY);
+			free(cp);
+			if (fd == -1) {
+				cmdq_error(cmdq, "bad working directory: %s",
+				    strerror(errno));
+				return (CMD_RETURN_ERROR);
+			}
+			close(s->cwd);
+			s->cwd = fd;
+		}
+
 		cmdq->client->session = s;
 		notify_attached_session_changed(cmdq->client);
 		session_update_activity(s);
@@ -86,11 +116,34 @@ cmd_attach_session(struct cmd_q *cmdq, const char* tflag, int dflag, int rflag)
 			return (CMD_RETURN_ERROR);
 		}
 
+		if (cflag != NULL) {
+			ft = format_create();
+			if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL)
+				format_client(ft, c);
+			format_session(ft, s);
+			format_winlink(ft, s, s->curw);
+			format_window_pane(ft, s->curw->window->active);
+			cp = format_expand(ft, cflag);
+			format_free(ft);
+
+			fd = open(cp, O_RDONLY|O_DIRECTORY);
+			free(cp);
+			if (fd == -1) {
+				cmdq_error(cmdq, "bad working directory: %s",
+				    strerror(errno));
+				return (CMD_RETURN_ERROR);
+			}
+			close(s->cwd);
+			s->cwd = fd;
+		}
+
 		if (rflag)
 			cmdq->client->flags |= CLIENT_READONLY;
 
-		if (dflag)
-			server_write_session(s, MSG_DETACH, NULL, 0);
+		if (dflag) {
+			server_write_session(s, MSG_DETACH, s->name,
+			    strlen(s->name) + 1);
+		}
 
 		update = options_get_string(&s->options, "update-environment");
 		environ_update(update, &cmdq->client->environ, &s->environ);
@@ -116,5 +169,5 @@ cmd_attach_session_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct args	*args = self->args;
 
 	return (cmd_attach_session(cmdq, args_get(args, 't'),
-	    args_has(args, 'd'), args_has(args, 'r')));
+	    args_has(args, 'd'), args_has(args, 'r'), args_get(args, 'c')));
 }
diff --git a/cmd-bind-key.c b/cmd-bind-key.c
index 71e79ea0..4ff3ac84 100644
--- a/cmd-bind-key.c
+++ b/cmd-bind-key.c
@@ -27,7 +27,6 @@
  * Bind a key to a command, this recurses through cmd_*.
  */
 
-enum cmd_retval	 cmd_bind_key_check(struct args *);
 enum cmd_retval	 cmd_bind_key_exec(struct cmd *, struct cmd_q *);
 
 enum cmd_retval	 cmd_bind_key_table(struct cmd *, struct cmd_q *, int);
@@ -38,23 +37,9 @@ const struct cmd_entry cmd_bind_key_entry = {
 	"[-cnr] [-t key-table] key command [arguments]",
 	0,
 	NULL,
-	cmd_bind_key_check,
 	cmd_bind_key_exec
 };
 
-enum cmd_retval
-cmd_bind_key_check(struct args *args)
-{
-	if (args_has(args, 't')) {
-		if (args->argc != 2 && args->argc != 3)
-			return (CMD_RETURN_ERROR);
-	} else {
-		if (args->argc < 2)
-			return (CMD_RETURN_ERROR);
-	}
-	return (CMD_RETURN_NORMAL);
-}
-
 enum cmd_retval
 cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
 {
@@ -63,6 +48,18 @@ cmd_bind_key_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct cmd_list	*cmdlist;
 	int		 key;
 
+	if (args_has(args, 't')) {
+		if (args->argc != 2 && args->argc != 3) {
+			cmdq_error(cmdq, "not enough arguments");
+			return (CMD_RETURN_ERROR);
+		}
+	} else {
+		if (args->argc < 2) {
+			cmdq_error(cmdq, "not enough arguments");
+			return (CMD_RETURN_ERROR);
+		}
+	}
+
 	key = key_string_lookup_string(args->argv[0]);
 	if (key == KEYC_NONE) {
 		cmdq_error(cmdq, "unknown key: %s", args->argv[0]);
diff --git a/cmd-break-pane.c b/cmd-break-pane.c
index 8ed9a1a6..d0a5a450 100644
--- a/cmd-break-pane.c
+++ b/cmd-break-pane.c
@@ -34,7 +34,6 @@ const struct cmd_entry cmd_break_pane_entry = {
 	"[-dP] [-F format] " CMD_TARGET_PANE_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_break_pane_exec
 };
 
diff --git a/cmd-capture-pane.c b/cmd-capture-pane.c
index f59dc2d6..e157e3cb 100644
--- a/cmd-capture-pane.c
+++ b/cmd-capture-pane.c
@@ -42,7 +42,6 @@ const struct cmd_entry cmd_capture_pane_entry = {
 	CMD_TARGET_PANE_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_capture_pane_exec
 };
 
diff --git a/cmd-choose-buffer.c b/cmd-choose-buffer.c
index e6b79d91..d79f6fdc 100644
--- a/cmd-choose-buffer.c
+++ b/cmd-choose-buffer.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_choose_buffer_entry = {
 	CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
 	0,
 	NULL,
-	NULL,
 	cmd_choose_buffer_exec
 };
 
diff --git a/cmd-choose-client.c b/cmd-choose-client.c
index 40752a70..93671987 100644
--- a/cmd-choose-client.c
+++ b/cmd-choose-client.c
@@ -37,7 +37,6 @@ const struct cmd_entry cmd_choose_client_entry = {
 	CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
 	0,
 	NULL,
-	NULL,
 	cmd_choose_client_exec
 };
 
diff --git a/cmd-choose-list.c b/cmd-choose-list.c
index 15f87294..c3caabba 100644
--- a/cmd-choose-list.c
+++ b/cmd-choose-list.c
@@ -39,7 +39,6 @@ const struct cmd_entry cmd_choose_list_entry = {
 	"[-l items] " CMD_TARGET_WINDOW_USAGE "[template]",
 	0,
 	NULL,
-	NULL,
 	cmd_choose_list_exec
 };
 
diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c
index a9b6ffbc..be3bd69a 100644
--- a/cmd-choose-tree.c
+++ b/cmd-choose-tree.c
@@ -41,7 +41,6 @@ const struct cmd_entry cmd_choose_tree_entry = {
 	"[-W format] " CMD_TARGET_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_choose_tree_exec
 };
 
@@ -51,7 +50,6 @@ const struct cmd_entry cmd_choose_session_entry = {
 	CMD_TARGET_WINDOW_USAGE " [-F format] [template]",
 	0,
 	NULL,
-	NULL,
 	cmd_choose_tree_exec
 };
 
@@ -61,7 +59,6 @@ const struct cmd_entry cmd_choose_window_entry = {
 	CMD_TARGET_WINDOW_USAGE "[-F format] [template]",
 	0,
 	NULL,
-	NULL,
 	cmd_choose_tree_exec
 };
 
@@ -228,7 +225,6 @@ windows_only:
 	free(final_win_template_last);
 
 	window_choose_ready(wl->window->active, cur_win, NULL);
-	window_choose_collapse_all(wl->window->active);
 
 	if (args_has(args, 'u')) {
 		window_choose_expand_all(wl->window->active);
diff --git a/cmd-clear-history.c b/cmd-clear-history.c
index aebaa27d..cce3ea18 100644
--- a/cmd-clear-history.c
+++ b/cmd-clear-history.c
@@ -32,7 +32,6 @@ const struct cmd_entry cmd_clear_history_entry = {
 	CMD_TARGET_PANE_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_clear_history_exec
 };
 
diff --git a/cmd-clock-mode.c b/cmd-clock-mode.c
index 872f3d53..50e4ab8b 100644
--- a/cmd-clock-mode.c
+++ b/cmd-clock-mode.c
@@ -32,7 +32,6 @@ const struct cmd_entry cmd_clock_mode_entry = {
 	CMD_TARGET_PANE_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_clock_mode_exec
 };
 
diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c
index 3bb79ed9..759d578b 100644
--- a/cmd-command-prompt.c
+++ b/cmd-command-prompt.c
@@ -30,7 +30,6 @@
  */
 
 void	cmd_command_prompt_key_binding(struct cmd *, int);
-int	cmd_command_prompt_check(struct args *);
 enum cmd_retval	cmd_command_prompt_exec(struct cmd *, struct cmd_q *);
 
 int	cmd_command_prompt_callback(void *, const char *);
@@ -42,7 +41,6 @@ const struct cmd_entry cmd_command_prompt_entry = {
 	"[-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " [template]",
 	0,
 	cmd_command_prompt_key_binding,
-	NULL,
 	cmd_command_prompt_exec
 };
 
diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c
index e670f69c..9266721f 100644
--- a/cmd-confirm-before.c
+++ b/cmd-confirm-before.c
@@ -38,7 +38,6 @@ const struct cmd_entry cmd_confirm_before_entry = {
 	"[-p prompt] " CMD_TARGET_CLIENT_USAGE " command",
 	0,
 	cmd_confirm_before_key_binding,
-	NULL,
 	cmd_confirm_before_exec
 };
 
diff --git a/cmd-copy-mode.c b/cmd-copy-mode.c
index 40584a28..f11c7aff 100644
--- a/cmd-copy-mode.c
+++ b/cmd-copy-mode.c
@@ -33,7 +33,6 @@ const struct cmd_entry cmd_copy_mode_entry = {
 	"[-u] " CMD_TARGET_PANE_USAGE,
 	0,
 	cmd_copy_mode_key_binding,
-	NULL,
 	cmd_copy_mode_exec
 };
 
diff --git a/cmd-delete-buffer.c b/cmd-delete-buffer.c
index bc3982ca..b8f55db4 100644
--- a/cmd-delete-buffer.c
+++ b/cmd-delete-buffer.c
@@ -34,7 +34,6 @@ const struct cmd_entry cmd_delete_buffer_entry = {
 	CMD_BUFFER_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_delete_buffer_exec
 };
 
diff --git a/cmd-detach-client.c b/cmd-detach-client.c
index 6e00e079..f0867364 100644
--- a/cmd-detach-client.c
+++ b/cmd-detach-client.c
@@ -18,6 +18,8 @@
 
 #include <sys/types.h>
 
+#include <string.h>
+
 #include "tmux.h"
 
 /*
@@ -32,7 +34,6 @@ const struct cmd_entry cmd_detach_client_entry = {
 	"[-P] [-a] [-s target-session] " CMD_TARGET_CLIENT_USAGE,
 	CMD_READONLY,
 	NULL,
-	NULL,
 	cmd_detach_client_exec
 };
 
@@ -41,8 +42,8 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq)
 {
 	struct args	*args = self->args;
 	struct client	*c, *c2;
-	struct session 	*s;
-	enum msgtype     msgtype;
+	struct session	*s;
+	enum msgtype	 msgtype;
 	u_int 		 i;
 
 	if (args_has(args, 'P'))
@@ -57,8 +58,10 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq)
 
 		for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
 			c = ARRAY_ITEM(&clients, i);
-			if (c != NULL && c->session == s)
-				server_write_client(c, msgtype, NULL, 0);
+			if (c == NULL || c->session != s)
+				continue;
+			server_write_client(c, msgtype, c->session->name,
+			    strlen(c->session->name) + 1);
 		}
 	} else {
 		c = cmd_find_client(cmdq, args_get(args, 't'), 0);
@@ -70,10 +73,14 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq)
 				c2 = ARRAY_ITEM(&clients, i);
 				if (c2 == NULL || c == c2)
 					continue;
-				server_write_client(c2, msgtype, NULL, 0);
+				server_write_client(c2, msgtype,
+				    c2->session->name,
+				    strlen(c2->session->name) + 1);
 			}
-		} else
-			server_write_client(c, msgtype, NULL, 0);
+		} else {
+			server_write_client(c, msgtype, c->session->name,
+			    strlen(c->session->name) + 1);
+		}
 	}
 
 	return (CMD_RETURN_STOP);
diff --git a/cmd-display-message.c b/cmd-display-message.c
index 485ccf08..78752f86 100644
--- a/cmd-display-message.c
+++ b/cmd-display-message.c
@@ -36,7 +36,6 @@ const struct cmd_entry cmd_display_message_entry = {
 	" [message]",
 	0,
 	NULL,
-	NULL,
 	cmd_display_message_exec
 };
 
@@ -71,9 +70,9 @@ cmd_display_message_exec(struct cmd *self, struct cmd_q *cmdq)
 	}
 
 	if (args_has(args, 'c')) {
-	    c = cmd_find_client(cmdq, args_get(args, 'c'), 0);
-	    if (c == NULL)
-		return (CMD_RETURN_ERROR);
+		c = cmd_find_client(cmdq, args_get(args, 'c'), 0);
+		if (c == NULL)
+			return (CMD_RETURN_ERROR);
 	} else {
 		c = cmd_current_client(cmdq);
 		if (c == NULL && !args_has(self->args, 'p')) {
diff --git a/cmd-display-panes.c b/cmd-display-panes.c
index 4a8731a4..c137feef 100644
--- a/cmd-display-panes.c
+++ b/cmd-display-panes.c
@@ -32,7 +32,6 @@ const struct cmd_entry cmd_display_panes_entry = {
 	CMD_TARGET_CLIENT_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_display_panes_exec
 };
 
diff --git a/cmd-find-window.c b/cmd-find-window.c
index 02f19307..647cc8fb 100644
--- a/cmd-find-window.c
+++ b/cmd-find-window.c
@@ -48,7 +48,6 @@ const struct cmd_entry cmd_find_window_entry = {
 	"[-CNT] [-F format] " CMD_TARGET_WINDOW_USAGE " match-string",
 	0,
 	NULL,
-	NULL,
 	cmd_find_window_exec
 };
 
diff --git a/cmd-has-session.c b/cmd-has-session.c
index c4286b86..38a92f61 100644
--- a/cmd-has-session.c
+++ b/cmd-has-session.c
@@ -32,7 +32,6 @@ const struct cmd_entry cmd_has_session_entry = {
 	CMD_TARGET_SESSION_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_has_session_exec
 };
 
diff --git a/cmd-if-shell.c b/cmd-if-shell.c
index d1cbd7f3..9b6dcf30 100644
--- a/cmd-if-shell.c
+++ b/cmd-if-shell.c
@@ -41,7 +41,6 @@ const struct cmd_entry cmd_if_shell_entry = {
 	"[-b] " CMD_TARGET_PANE_USAGE " shell-command command [command]",
 	0,
 	NULL,
-	NULL,
 	cmd_if_shell_exec
 };
 
@@ -148,6 +147,9 @@ cmd_if_shell_done(struct cmd_q *cmdq1)
 	struct cmd_if_shell_data	*cdata = cmdq1->data;
 	struct cmd_q			*cmdq = cdata->cmdq;
 
+	if (cmdq1->client_exit >= 0)
+		cmdq->client_exit = cmdq1->client_exit;
+
 	if (!cmdq_free(cmdq) && !cdata->bflag)
 		cmdq_continue(cmdq);
 
diff --git a/cmd-join-pane.c b/cmd-join-pane.c
index 2cf587e0..1a710cec 100644
--- a/cmd-join-pane.c
+++ b/cmd-join-pane.c
@@ -39,7 +39,6 @@ const struct cmd_entry cmd_join_pane_entry = {
 	"[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]",
 	0,
 	cmd_join_pane_key_binding,
-	NULL,
 	cmd_join_pane_exec
 };
 
@@ -49,7 +48,6 @@ const struct cmd_entry cmd_move_pane_entry = {
 	"[-bdhv] [-p percentage|-l size] [-s src-pane] [-t dst-pane]",
 	0,
 	NULL,
-	NULL,
 	cmd_join_pane_exec
 };
 
diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c
index 40761350..bf486eb3 100644
--- a/cmd-kill-pane.c
+++ b/cmd-kill-pane.c
@@ -34,7 +34,6 @@ const struct cmd_entry cmd_kill_pane_entry = {
 	"[-a] " CMD_TARGET_PANE_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_kill_pane_exec
 };
 
diff --git a/cmd-kill-server.c b/cmd-kill-server.c
index a6065460..ba63faa3 100644
--- a/cmd-kill-server.c
+++ b/cmd-kill-server.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_kill_server_entry = {
 	"",
 	0,
 	NULL,
-	NULL,
 	cmd_kill_server_exec
 };
 
diff --git a/cmd-kill-session.c b/cmd-kill-session.c
index a12cc8a4..54b0c31b 100644
--- a/cmd-kill-session.c
+++ b/cmd-kill-session.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_kill_session_entry = {
 	"[-a] " CMD_TARGET_SESSION_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_kill_session_exec
 };
 
diff --git a/cmd-kill-window.c b/cmd-kill-window.c
index 34b97499..ca8fe3d6 100644
--- a/cmd-kill-window.c
+++ b/cmd-kill-window.c
@@ -32,7 +32,6 @@ const struct cmd_entry cmd_kill_window_entry = {
 	"[-a] " CMD_TARGET_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_kill_window_exec
 };
 
diff --git a/cmd-link-window.c b/cmd-link-window.c
index 2be8ace0..d94eb38a 100644
--- a/cmd-link-window.c
+++ b/cmd-link-window.c
@@ -34,7 +34,6 @@ const struct cmd_entry cmd_link_window_entry = {
 	"[-dk] " CMD_SRCDST_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_link_window_exec
 };
 
diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c
index 58af0020..02a4183e 100644
--- a/cmd-list-buffers.c
+++ b/cmd-list-buffers.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_list_buffers_entry = {
 	"[-F format]",
 	0,
 	NULL,
-	NULL,
 	cmd_list_buffers_exec
 };
 
diff --git a/cmd-list-clients.c b/cmd-list-clients.c
index 59f63099..904ec005 100644
--- a/cmd-list-clients.c
+++ b/cmd-list-clients.c
@@ -36,7 +36,6 @@ const struct cmd_entry cmd_list_clients_entry = {
 	"[-F format] " CMD_TARGET_SESSION_USAGE,
 	CMD_READONLY,
 	NULL,
-	NULL,
 	cmd_list_clients_exec
 };
 
diff --git a/cmd-list-commands.c b/cmd-list-commands.c
index 1bf6e703..06e48378 100644
--- a/cmd-list-commands.c
+++ b/cmd-list-commands.c
@@ -32,7 +32,6 @@ const struct cmd_entry cmd_list_commands_entry = {
 	"",
 	0,
 	NULL,
-	NULL,
 	cmd_list_commands_exec
 };
 
diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index 78998b66..615c5ce1 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_list_keys_entry = {
 	"[-t key-table]",
 	0,
 	NULL,
-	NULL,
 	cmd_list_keys_exec
 };
 
diff --git a/cmd-list-panes.c b/cmd-list-panes.c
index 0d498e28..c989eba1 100644
--- a/cmd-list-panes.c
+++ b/cmd-list-panes.c
@@ -41,7 +41,6 @@ const struct cmd_entry cmd_list_panes_entry = {
 	"[-as] [-F format] " CMD_TARGET_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_list_panes_exec
 };
 
diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c
index 61c12f4e..a3613c87 100644
--- a/cmd-list-sessions.c
+++ b/cmd-list-sessions.c
@@ -36,7 +36,6 @@ const struct cmd_entry cmd_list_sessions_entry = {
 	"[-F format]",
 	0,
 	NULL,
-	NULL,
 	cmd_list_sessions_exec
 };
 
diff --git a/cmd-list-windows.c b/cmd-list-windows.c
index 5c2a2b95..ce122f45 100644
--- a/cmd-list-windows.c
+++ b/cmd-list-windows.c
@@ -39,7 +39,6 @@ const struct cmd_entry cmd_list_windows_entry = {
 	"[-a] [-F format] " CMD_TARGET_SESSION_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_list_windows_exec
 };
 
diff --git a/cmd-load-buffer.c b/cmd-load-buffer.c
index 3be14d6a..4acbab5e 100644
--- a/cmd-load-buffer.c
+++ b/cmd-load-buffer.c
@@ -19,6 +19,7 @@
 #include <sys/types.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -39,7 +40,6 @@ const struct cmd_entry cmd_load_buffer_entry = {
 	CMD_BUFFER_USAGE " path",
 	0,
 	NULL,
-	NULL,
 	cmd_load_buffer_exec
 };
 
@@ -50,11 +50,11 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct client	*c = cmdq->client;
 	struct session  *s;
 	FILE		*f;
-	const char	*path, *newpath, *wd;
+	const char	*path;
 	char		*pdata, *new_pdata, *cause;
 	size_t		 psize;
 	u_int		 limit;
-	int		 ch, error, buffer, *buffer_ptr;
+	int		 ch, error, buffer, *buffer_ptr, cwd, fd;
 
 	if (!args_has(args, 'b'))
 		buffer = -1;
@@ -82,20 +82,17 @@ cmd_load_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 		return (CMD_RETURN_WAIT);
 	}
 
-	if (c != NULL)
-		wd = c->cwd;
-	else if ((s = cmd_current_session(cmdq, 0)) != NULL) {
-		wd = options_get_string(&s->options, "default-path");
-		if (*wd == '\0')
-			wd = s->cwd;
-	} else
-		wd = NULL;
-	if (wd != NULL && *wd != '\0') {
-		newpath = get_full_path(wd, path);
-		if (newpath != NULL)
-			path = newpath;
-	}
-	if ((f = fopen(path, "rb")) == NULL) {
+	if (c != NULL && c->session == NULL)
+		cwd = c->cwd;
+	else if ((s = cmd_current_session(cmdq, 0)) != NULL)
+		cwd = s->cwd;
+	else
+		cwd = AT_FDCWD;
+
+	if ((fd = openat(cwd, path, O_RDONLY)) == -1 ||
+	    (f = fdopen(fd, "rb")) == NULL) {
+		if (fd != -1)
+			close(fd);
 		cmdq_error(cmdq, "%s: %s", path, strerror(errno));
 		return (CMD_RETURN_ERROR);
 	}
diff --git a/cmd-lock-server.c b/cmd-lock-server.c
index 0b6aafe8..2b591ecf 100644
--- a/cmd-lock-server.c
+++ b/cmd-lock-server.c
@@ -36,7 +36,6 @@ const struct cmd_entry cmd_lock_server_entry = {
 	"",
 	0,
 	NULL,
-	NULL,
 	cmd_lock_server_exec
 };
 
@@ -46,7 +45,6 @@ const struct cmd_entry cmd_lock_session_entry = {
 	CMD_TARGET_SESSION_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_lock_server_exec
 };
 
@@ -56,7 +54,6 @@ const struct cmd_entry cmd_lock_client_entry = {
 	CMD_TARGET_CLIENT_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_lock_server_exec
 };
 
diff --git a/cmd-move-window.c b/cmd-move-window.c
index 1a147c7e..21606755 100644
--- a/cmd-move-window.c
+++ b/cmd-move-window.c
@@ -34,7 +34,6 @@ const struct cmd_entry cmd_move_window_entry = {
 	"[-dkr] " CMD_SRCDST_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_move_window_exec
 };
 
diff --git a/cmd-new-session.c b/cmd-new-session.c
index 4eebe632..ad083a44 100644
--- a/cmd-new-session.c
+++ b/cmd-new-session.c
@@ -18,6 +18,8 @@
 
 #include <sys/types.h>
 
+#include <errno.h>
+#include <fcntl.h>
 #include <pwd.h>
 #include <stdlib.h>
 #include <string.h>
@@ -30,46 +32,39 @@
  * Create a new session and attach to the current terminal unless -d is given.
  */
 
-enum cmd_retval	 cmd_new_session_check(struct args *);
 enum cmd_retval	 cmd_new_session_exec(struct cmd *, struct cmd_q *);
 
 const struct cmd_entry cmd_new_session_entry = {
 	"new-session", "new",
-	"AdDF:n:Ps:t:x:y:", 0, 1,
-	"[-AdDP] [-F format] [-n window-name] [-s session-name] "
-	CMD_TARGET_SESSION_USAGE " [-x width] [-y height] [command]",
-	CMD_STARTSERVER|CMD_CANTNEST|CMD_SENDENVIRON,
+	"Ac:dDF:n:Ps:t:x:y:", 0, 1,
+	"[-AdDP] [-c start-directory] [-F format] [-n window-name] "
+	"[-s session-name] " CMD_TARGET_SESSION_USAGE " [-x width] [-y height] "
+	"[command]",
+	CMD_STARTSERVER|CMD_CANTNEST,
 	NULL,
-	cmd_new_session_check,
 	cmd_new_session_exec
 };
 
-enum cmd_retval
-cmd_new_session_check(struct args *args)
-{
-	if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n')))
-		return (CMD_RETURN_ERROR);
-	return (CMD_RETURN_NORMAL);
-}
-
 enum cmd_retval
 cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
 {
 	struct args		*args = self->args;
-	struct client		*c = cmdq->client;
+	struct client		*c = cmdq->client, *c0;
 	struct session		*s, *groupwith;
 	struct window		*w;
 	struct environ		 env;
 	struct termios		 tio, *tiop;
-	struct passwd		*pw;
-	const char		*newname, *target, *update, *cwd, *errstr;
-	const char		*template;
+	const char		*newname, *target, *update, *errstr, *template;
 	char			*cmd, *cause, *cp;
-	int			 detached, idx;
+	int			 detached, already_attached, idx, cwd, fd = -1;
 	u_int			 sx, sy;
-	int			 already_attached;
 	struct format_tree	*ft;
 
+	if (args_has(args, 't') && (args->argc != 0 || args_has(args, 'n'))) {
+		cmdq_error(cmdq, "command or window name given with target");
+		return (CMD_RETURN_ERROR);
+	}
+
 	newname = args_get(args, 's');
 	if (newname != NULL) {
 		if (!session_check_name(newname)) {
@@ -79,7 +74,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
 		if (session_find(newname) != NULL) {
 			if (args_has(args, 'A')) {
 				return (cmd_attach_session(cmdq, newname,
-				    args_has(args, 'D'), 0));
+				    args_has(args, 'D'), 0, NULL));
 			}
 			cmdq_error(cmdq, "duplicate session: %s", newname);
 			return (CMD_RETURN_ERROR);
@@ -104,6 +99,31 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
 	if (c != NULL && c->session != NULL)
 		already_attached = 1;
 
+	/* Get the new session working directory. */
+	if (args_has(args, 'c')) {
+		ft = format_create();
+		if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL)
+			format_client(ft, c0);
+		cp = format_expand(ft, args_get(args, 'c'));
+		format_free(ft);
+
+		fd = open(cp, O_RDONLY|O_DIRECTORY);
+		free(cp);
+		if (fd == -1) {
+			cmdq_error(cmdq, "bad working directory: %s",
+			    strerror(errno));
+			return (CMD_RETURN_ERROR);
+		}
+		cwd = fd;
+	} else if (c != NULL && c->session == NULL)
+		cwd = c->cwd;
+	else if ((c0 = cmd_current_client(cmdq)) != NULL)
+		cwd = c0->session->cwd;
+	else {
+		fd = open(".", O_RDONLY);
+		cwd = fd;
+	}
+
 	/*
 	 * Save the termios settings, part of which is used for new windows in
 	 * this session.
@@ -125,21 +145,10 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
 		if (server_client_open(c, NULL, &cause) != 0) {
 			cmdq_error(cmdq, "open terminal failed: %s", cause);
 			free(cause);
-			return (CMD_RETURN_ERROR);
+			goto error;
 		}
 	}
 
-	/* Get the new session working directory. */
-	if (c != NULL && c->cwd != NULL)
-		cwd = c->cwd;
-	else {
-		pw = getpwuid(getuid());
-		if (pw->pw_dir != NULL && *pw->pw_dir != '\0')
-			cwd = pw->pw_dir;
-		else
-			cwd = "/";
-	}
-
 	/* Find new session size. */
 	if (c != NULL) {
 		sx = c->tty.sx;
@@ -152,14 +161,14 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
 		sx = strtonum(args_get(args, 'x'), 1, USHRT_MAX, &errstr);
 		if (errstr != NULL) {
 			cmdq_error(cmdq, "width %s", errstr);
-			return (CMD_RETURN_ERROR);
+			goto error;
 		}
 	}
 	if (detached && args_has(args, 'y')) {
 		sy = strtonum(args_get(args, 'y'), 1, USHRT_MAX, &errstr);
 		if (errstr != NULL) {
 			cmdq_error(cmdq, "height %s", errstr);
-			return (CMD_RETURN_ERROR);
+			goto error;
 		}
 	}
 	if (sy > 0 && options_get_number(&global_s_options, "status"))
@@ -189,7 +198,7 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
 	if (s == NULL) {
 		cmdq_error(cmdq, "create session failed: %s", cause);
 		free(cause);
-		return (CMD_RETURN_ERROR);
+		goto error;
 	}
 	environ_free(&env);
 
@@ -240,8 +249,8 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
 			template = NEW_SESSION_TEMPLATE;
 
 		ft = format_create();
-		if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL)
-			format_client(ft, c);
+		if ((c0 = cmd_find_client(cmdq, NULL, 1)) != NULL)
+			format_client(ft, c0);
 		format_session(ft, s);
 
 		cp = format_expand(ft, template);
@@ -253,5 +262,13 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq)
 
 	if (!detached)
 		cmdq->client_exit = 0;
+
+	if (fd != -1)
+		close(fd);
 	return (CMD_RETURN_NORMAL);
+
+error:
+	if (fd != -1)
+		close(fd);
+	return (CMD_RETURN_ERROR);
 }
diff --git a/cmd-new-window.c b/cmd-new-window.c
index cfc0b8bd..5c2cbe40 100644
--- a/cmd-new-window.c
+++ b/cmd-new-window.c
@@ -18,7 +18,11 @@
 
 #include <sys/types.h>
 
+#include <errno.h>
+#include <fcntl.h>
 #include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 
 #include "tmux.h"
 
@@ -35,7 +39,6 @@ const struct cmd_entry cmd_new_window_entry = {
 	CMD_TARGET_WINDOW_USAGE " [command]",
 	0,
 	NULL,
-	NULL,
 	cmd_new_window_exec
 };
 
@@ -46,9 +49,9 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct session		*s;
 	struct winlink		*wl;
 	struct client		*c;
-	const char		*cmd, *cwd, *template;
+	const char		*cmd, *template;
 	char			*cause, *cp;
-	int			 idx, last, detached;
+	int			 idx, last, detached, cwd, fd = -1;
 	struct format_tree	*ft;
 
 	if (args_has(args, 'a')) {
@@ -103,7 +106,29 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq)
 		cmd = options_get_string(&s->options, "default-command");
 	else
 		cmd = args->argv[0];
-	cwd = cmd_get_default_path(cmdq, args_get(args, 'c'));
+
+	if (args_has(args, 'c')) {
+		ft = format_create();
+		if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL)
+			format_client(ft, c);
+		format_session(ft, s);
+		format_winlink(ft, s, s->curw);
+		format_window_pane(ft, s->curw->window->active);
+		cp = format_expand(ft, args_get(args, 'c'));
+		format_free(ft);
+
+		fd = open(cp, O_RDONLY|O_DIRECTORY);
+		free(cp);
+		if (fd == -1) {
+			cmdq_error(cmdq, "bad working directory: %s",
+			    strerror(errno));
+			return (CMD_RETURN_ERROR);
+		}
+		cwd = fd;
+	} else if (cmdq->client != NULL && cmdq->client->session == NULL)
+		cwd = cmdq->client->cwd;
+	else
+		cwd = s->cwd;
 
 	if (idx == -1)
 		idx = -1 - options_get_number(&s->options, "base-index");
@@ -111,7 +136,7 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq)
 	if (wl == NULL) {
 		cmdq_error(cmdq, "create window failed: %s", cause);
 		free(cause);
-		return (CMD_RETURN_ERROR);
+		goto error;
 	}
 	if (!detached) {
 		session_select(s, wl->idx);
@@ -137,5 +162,12 @@ cmd_new_window_exec(struct cmd *self, struct cmd_q *cmdq)
 		format_free(ft);
 	}
 
+	if (fd != -1)
+		close(fd);
 	return (CMD_RETURN_NORMAL);
+
+error:
+	if (fd != -1)
+		close(fd);
+	return (CMD_RETURN_ERROR);
 }
diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c
index b07c9faf..5305b7e6 100644
--- a/cmd-paste-buffer.c
+++ b/cmd-paste-buffer.c
@@ -38,7 +38,6 @@ const struct cmd_entry cmd_paste_buffer_entry = {
 	"[-dpr] [-s separator] [-b buffer-index] " CMD_TARGET_PANE_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_paste_buffer_exec
 };
 
diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c
index aa72c699..611ad8cf 100644
--- a/cmd-pipe-pane.c
+++ b/cmd-pipe-pane.c
@@ -41,7 +41,6 @@ const struct cmd_entry cmd_pipe_pane_entry = {
 	"[-o] " CMD_TARGET_PANE_USAGE " [command]",
 	0,
 	NULL,
-	NULL,
 	cmd_pipe_pane_exec
 };
 
diff --git a/cmd-queue.c b/cmd-queue.c
index 243f73dd..c5905bdb 100644
--- a/cmd-queue.c
+++ b/cmd-queue.c
@@ -35,7 +35,7 @@ cmdq_new(struct client *c)
 	cmdq->dead = 0;
 
 	cmdq->client = c;
-	cmdq->client_exit = 0;
+	cmdq->client_exit = -1;
 
 	TAILQ_INIT(&cmdq->queue);
 	cmdq->item = NULL;
@@ -143,7 +143,7 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...)
 		evbuffer_add(c->stderr_data, "\n", 1);
 
 		server_push_stderr(c);
-		c->retcode = 1;
+		c->retval = 1;
 	} else {
 		*msg = toupper((u_char) *msg);
 		status_message_set(c, "%s", msg);
@@ -154,18 +154,15 @@ cmdq_error(struct cmd_q *cmdq, const char *fmt, ...)
 
 /* Print a guard line. */
 int
-cmdq_guard(struct cmd_q *cmdq, const char *guard)
+cmdq_guard(struct cmd_q *cmdq, const char *guard, int flags)
 {
 	struct client	*c = cmdq->client;
-	int		 flags;
 
 	if (c == NULL)
 		return 0;
 	if (!(c->flags & CLIENT_CONTROL))
 		return 0;
 
-	flags = !!(cmdq->cmd->flags & CMD_CONTROL);
-
 	evbuffer_add_printf(c->stdout_data, "%%%s %ld %u %d\n", guard,
 	    (long) cmdq->time, cmdq->number, flags);
 	server_push_stdout(c);
@@ -202,7 +199,7 @@ cmdq_continue(struct cmd_q *cmdq)
 {
 	struct cmd_q_item	*next;
 	enum cmd_retval		 retval;
-	int			 empty, guard;
+	int			 empty, guard, flags;
 	char			 s[1024];
 
 	notify_disable();
@@ -228,13 +225,16 @@ cmdq_continue(struct cmd_q *cmdq)
 			cmdq->time = time(NULL);
 			cmdq->number++;
 
-			guard = cmdq_guard(cmdq, "begin");
+			flags = !!(cmdq->cmd->flags & CMD_CONTROL);
+			guard = cmdq_guard(cmdq, "begin", flags);
+
 			retval = cmdq->cmd->entry->exec(cmdq->cmd, cmdq);
+
 			if (guard) {
 				if (retval == CMD_RETURN_ERROR)
-				    cmdq_guard(cmdq, "error");
+					cmdq_guard(cmdq, "error", flags);
 				else
-				    cmdq_guard(cmdq, "end");
+					cmdq_guard(cmdq, "end", flags);
 			}
 
 			if (retval == CMD_RETURN_ERROR)
@@ -259,7 +259,7 @@ cmdq_continue(struct cmd_q *cmdq)
 	} while (cmdq->item != NULL);
 
 empty:
-	if (cmdq->client_exit)
+	if (cmdq->client_exit > 0)
 		cmdq->client->flags |= CLIENT_EXIT;
 	if (cmdq->emptyfn != NULL)
 		cmdq->emptyfn(cmdq); /* may free cmdq */
diff --git a/cmd-refresh-client.c b/cmd-refresh-client.c
index 7d9d539f..45804fbe 100644
--- a/cmd-refresh-client.c
+++ b/cmd-refresh-client.c
@@ -29,10 +29,9 @@ enum cmd_retval	 cmd_refresh_client_exec(struct cmd *, struct cmd_q *);
 const struct cmd_entry cmd_refresh_client_entry = {
 	"refresh-client", "refresh",
 	"C:St:", 0, 0,
-	"[-S] [-C size]" CMD_TARGET_CLIENT_USAGE,
+	"[-S] [-C size] " CMD_TARGET_CLIENT_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_refresh_client_exec
 };
 
diff --git a/cmd-rename-session.c b/cmd-rename-session.c
index 3f8a9d8f..4c8ae122 100644
--- a/cmd-rename-session.c
+++ b/cmd-rename-session.c
@@ -34,7 +34,6 @@ const struct cmd_entry cmd_rename_session_entry = {
 	CMD_TARGET_SESSION_USAGE " new-name",
 	0,
 	NULL,
-	NULL,
 	cmd_rename_session_exec
 };
 
diff --git a/cmd-rename-window.c b/cmd-rename-window.c
index c756ba1f..f647abb3 100644
--- a/cmd-rename-window.c
+++ b/cmd-rename-window.c
@@ -34,7 +34,6 @@ const struct cmd_entry cmd_rename_window_entry = {
 	CMD_TARGET_WINDOW_USAGE " new-name",
 	0,
 	NULL,
-	NULL,
 	cmd_rename_window_exec
 };
 
diff --git a/cmd-resize-pane.c b/cmd-resize-pane.c
index ca2a6cd3..d31f8868 100644
--- a/cmd-resize-pane.c
+++ b/cmd-resize-pane.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_resize_pane_entry = {
 	"[-DLRUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " [adjustment]",
 	0,
 	cmd_resize_pane_key_binding,
-	NULL,
 	cmd_resize_pane_exec
 };
 
diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c
index 4486c91f..bcde2754 100644
--- a/cmd-respawn-pane.c
+++ b/cmd-respawn-pane.c
@@ -36,7 +36,6 @@ const struct cmd_entry cmd_respawn_pane_entry = {
 	"[-k] " CMD_TARGET_PANE_USAGE " [command]",
 	0,
 	NULL,
-	NULL,
 	cmd_respawn_pane_exec
 };
 
@@ -78,7 +77,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq)
 		cmd = args->argv[0];
 	else
 		cmd = NULL;
-	if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) {
+	if (window_pane_spawn(wp, cmd, NULL, -1, &env, s->tio, &cause) != 0) {
 		cmdq_error(cmdq, "respawn pane failed: %s", cause);
 		free(cause);
 		environ_free(&env);
diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c
index 35bd3471..e6d913cf 100644
--- a/cmd-respawn-window.c
+++ b/cmd-respawn-window.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_respawn_window_entry = {
 	"[-k] " CMD_TARGET_WINDOW_USAGE " [command]",
 	0,
 	NULL,
-	NULL,
 	cmd_respawn_window_exec
 };
 
@@ -80,7 +79,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq)
 		cmd = args->argv[0];
 	else
 		cmd = NULL;
-	if (window_pane_spawn(wp, cmd, NULL, NULL, &env, s->tio, &cause) != 0) {
+	if (window_pane_spawn(wp, cmd, NULL, -1, &env, s->tio, &cause) != 0) {
 		cmdq_error(cmdq, "respawn window failed: %s", cause);
 		free(cause);
 		environ_free(&env);
diff --git a/cmd-rotate-window.c b/cmd-rotate-window.c
index 75ca7292..9f4cc751 100644
--- a/cmd-rotate-window.c
+++ b/cmd-rotate-window.c
@@ -33,7 +33,6 @@ const struct cmd_entry cmd_rotate_window_entry = {
 	"[-DU] " CMD_TARGET_WINDOW_USAGE,
 	0,
 	cmd_rotate_window_key_binding,
-	NULL,
 	cmd_rotate_window_exec
 };
 
diff --git a/cmd-run-shell.c b/cmd-run-shell.c
index 7c7d333c..f5231814 100644
--- a/cmd-run-shell.c
+++ b/cmd-run-shell.c
@@ -41,7 +41,6 @@ const struct cmd_entry cmd_run_shell_entry = {
 	"[-b] " CMD_TARGET_PANE_USAGE " shell-command",
 	0,
 	NULL,
-	NULL,
 	cmd_run_shell_exec
 };
 
diff --git a/cmd-save-buffer.c b/cmd-save-buffer.c
index 52914a94..fc56dd8f 100644
--- a/cmd-save-buffer.c
+++ b/cmd-save-buffer.c
@@ -20,8 +20,11 @@
 #include <sys/stat.h>
 
 #include <errno.h>
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+#include <vis.h>
 
 #include "tmux.h"
 
@@ -37,7 +40,6 @@ const struct cmd_entry cmd_save_buffer_entry = {
 	"[-a] " CMD_BUFFER_USAGE " path",
 	0,
 	NULL,
-	NULL,
 	cmd_save_buffer_exec
 };
 
@@ -47,7 +49,6 @@ const struct cmd_entry cmd_show_buffer_entry = {
 	CMD_BUFFER_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_save_buffer_exec
 };
 
@@ -55,17 +56,14 @@ enum cmd_retval
 cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 {
 	struct args		*args = self->args;
-	struct client		*c;
+	struct client		*c = cmdq->client;
 	struct session          *s;
 	struct paste_buffer	*pb;
-	const char		*path, *newpath, *wd;
-	char			*cause, *start, *end;
-	size_t			 size, used;
-	int			 buffer;
-	mode_t			 mask;
+	const char		*path;
+	char			*cause, *start, *end, *msg;
+	size_t			 size, used, msglen;
+	int			 cwd, fd, buffer;
 	FILE			*f;
-	char			*msg;
-	size_t			 msglen;
 
 	if (!args_has(args, 'b')) {
 		if ((pb = paste_get_top(&global_buffers)) == NULL) {
@@ -92,7 +90,6 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 	else
 		path = args->argv[0];
 	if (strcmp(path, "-") == 0) {
-		c = cmdq->client;
 		if (c == NULL) {
 			cmdq_error(cmdq, "can't write to stdout");
 			return (CMD_RETURN_ERROR);
@@ -102,28 +99,26 @@ cmd_save_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
 		goto do_print;
 	}
 
-	c = cmdq->client;
-	if (c != NULL)
-		wd = c->cwd;
-	else if ((s = cmd_current_session(cmdq, 0)) != NULL) {
-		wd = options_get_string(&s->options, "default-path");
-		if (*wd == '\0')
-			wd = s->cwd;
-	} else
-		wd = NULL;
-	if (wd != NULL && *wd != '\0') {
-		newpath = get_full_path(wd, path);
-		if (newpath != NULL)
-			path = newpath;
-	}
-
-	mask = umask(S_IRWXG | S_IRWXO);
-	if (args_has(self->args, 'a'))
-		f = fopen(path, "ab");
+	if (c != NULL && c->session == NULL)
+		cwd = c->cwd;
+	else if ((s = cmd_current_session(cmdq, 0)) != NULL)
+		cwd = s->cwd;
 	else
-		f = fopen(path, "wb");
-	umask(mask);
+		cwd = AT_FDCWD;
+
+	f = NULL;
+	if (args_has(self->args, 'a')) {
+		fd = openat(cwd, path, O_CREAT|O_RDWR|O_APPEND, 0600);
+		if (fd != -1)
+			f = fdopen(fd, "ab");
+	} else {
+		fd = openat(cwd, path, O_CREAT|O_RDWR, 0600);
+		if (fd != -1)
+			f = fdopen(fd, "wb");
+	}
 	if (f == NULL) {
+		if (fd != -1)
+			close(fd);
 		cmdq_error(cmdq, "%s: %s", path, strerror(errno));
 		return (CMD_RETURN_ERROR);
 	}
diff --git a/cmd-select-layout.c b/cmd-select-layout.c
index ae1be4c4..053f3e40 100644
--- a/cmd-select-layout.c
+++ b/cmd-select-layout.c
@@ -33,7 +33,6 @@ const struct cmd_entry cmd_select_layout_entry = {
 	"[-np] " CMD_TARGET_WINDOW_USAGE " [layout-name]",
 	0,
 	cmd_select_layout_key_binding,
-	NULL,
 	cmd_select_layout_exec
 };
 
@@ -43,7 +42,6 @@ const struct cmd_entry cmd_next_layout_entry = {
 	CMD_TARGET_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_select_layout_exec
 };
 
@@ -53,7 +51,6 @@ const struct cmd_entry cmd_previous_layout_entry = {
 	CMD_TARGET_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_select_layout_exec
 };
 
diff --git a/cmd-select-pane.c b/cmd-select-pane.c
index b8a12671..c342fef3 100644
--- a/cmd-select-pane.c
+++ b/cmd-select-pane.c
@@ -33,7 +33,6 @@ const struct cmd_entry cmd_select_pane_entry = {
 	"[-lDLRU] " CMD_TARGET_PANE_USAGE,
 	0,
 	cmd_select_pane_key_binding,
-	NULL,
 	cmd_select_pane_exec
 };
 
@@ -43,7 +42,6 @@ const struct cmd_entry cmd_last_pane_entry = {
 	CMD_TARGET_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_select_pane_exec
 };
 
diff --git a/cmd-select-window.c b/cmd-select-window.c
index c15d5858..73196200 100644
--- a/cmd-select-window.c
+++ b/cmd-select-window.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_select_window_entry = {
 	"[-lnpT] " CMD_TARGET_WINDOW_USAGE,
 	0,
 	cmd_select_window_key_binding,
-	NULL,
 	cmd_select_window_exec
 };
 
@@ -45,7 +44,6 @@ const struct cmd_entry cmd_next_window_entry = {
 	"[-a] " CMD_TARGET_SESSION_USAGE,
 	0,
 	cmd_select_window_key_binding,
-	NULL,
 	cmd_select_window_exec
 };
 
@@ -55,7 +53,6 @@ const struct cmd_entry cmd_previous_window_entry = {
 	"[-a] " CMD_TARGET_SESSION_USAGE,
 	0,
 	cmd_select_window_key_binding,
-	NULL,
 	cmd_select_window_exec
 };
 
@@ -65,7 +62,6 @@ const struct cmd_entry cmd_last_window_entry = {
 	CMD_TARGET_SESSION_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_select_window_exec
 };
 
diff --git a/cmd-send-keys.c b/cmd-send-keys.c
index a2041656..50cb70e2 100644
--- a/cmd-send-keys.c
+++ b/cmd-send-keys.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_send_keys_entry = {
 	"[-lR] " CMD_TARGET_PANE_USAGE " key ...",
 	0,
 	NULL,
-	NULL,
 	cmd_send_keys_exec
 };
 
@@ -45,7 +44,6 @@ const struct cmd_entry cmd_send_prefix_entry = {
 	"[-2] " CMD_TARGET_PANE_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_send_keys_exec
 };
 
diff --git a/cmd-server-info.c b/cmd-server-info.c
index 8eba172a..3aa5df8a 100644
--- a/cmd-server-info.c
+++ b/cmd-server-info.c
@@ -38,7 +38,6 @@ const struct cmd_entry cmd_server_info_entry = {
 	"",
 	0,
 	NULL,
-	NULL,
 	cmd_server_info_exec
 };
 
diff --git a/cmd-set-buffer.c b/cmd-set-buffer.c
index 46d28ff2..fade4fe3 100644
--- a/cmd-set-buffer.c
+++ b/cmd-set-buffer.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_set_buffer_entry = {
 	CMD_BUFFER_USAGE " data",
 	0,
 	NULL,
-	NULL,
 	cmd_set_buffer_exec
 };
 
diff --git a/cmd-set-environment.c b/cmd-set-environment.c
index 0f0365aa..8d067c35 100644
--- a/cmd-set-environment.c
+++ b/cmd-set-environment.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_set_environment_entry = {
 	"[-gru] " CMD_TARGET_SESSION_USAGE " name [value]",
 	0,
 	NULL,
-	NULL,
 	cmd_set_environment_exec
 };
 
diff --git a/cmd-set-option.c b/cmd-set-option.c
index c246743c..3acf125d 100644
--- a/cmd-set-option.c
+++ b/cmd-set-option.c
@@ -67,7 +67,6 @@ const struct cmd_entry cmd_set_option_entry = {
 	"[-agosquw] [-t target-session|target-window] option [value]",
 	0,
 	NULL,
-	NULL,
 	cmd_set_option_exec
 };
 
@@ -77,7 +76,6 @@ const struct cmd_entry cmd_set_window_option_entry = {
 	"[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]",
 	0,
 	NULL,
-	NULL,
 	cmd_set_option_exec
 };
 
diff --git a/cmd-show-environment.c b/cmd-show-environment.c
index ffe98bcc..2238929e 100644
--- a/cmd-show-environment.c
+++ b/cmd-show-environment.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_show_environment_entry = {
 	"[-g] " CMD_TARGET_SESSION_USAGE " [name]",
 	0,
 	NULL,
-	NULL,
 	cmd_show_environment_exec
 };
 
diff --git a/cmd-show-messages.c b/cmd-show-messages.c
index bc2424ad..256570cd 100644
--- a/cmd-show-messages.c
+++ b/cmd-show-messages.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_show_messages_entry = {
 	CMD_TARGET_CLIENT_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_show_messages_exec
 };
 
diff --git a/cmd-show-options.c b/cmd-show-options.c
index e2f78e12..943353f6 100644
--- a/cmd-show-options.c
+++ b/cmd-show-options.c
@@ -40,7 +40,6 @@ const struct cmd_entry cmd_show_options_entry = {
 	"[-gqsvw] [-t target-session|target-window] [option]",
 	0,
 	NULL,
-	NULL,
 	cmd_show_options_exec
 };
 
@@ -50,7 +49,6 @@ const struct cmd_entry cmd_show_window_options_entry = {
 	"[-gv] " CMD_TARGET_WINDOW_USAGE " [option]",
 	0,
 	NULL,
-	NULL,
 	cmd_show_options_exec
 };
 
diff --git a/cmd-source-file.c b/cmd-source-file.c
index 45a3a39b..eb7e1490 100644
--- a/cmd-source-file.c
+++ b/cmd-source-file.c
@@ -37,7 +37,6 @@ const struct cmd_entry cmd_source_file_entry = {
 	"path",
 	0,
 	NULL,
-	NULL,
 	cmd_source_file_exec
 };
 
@@ -96,6 +95,9 @@ cmd_source_file_done(struct cmd_q *cmdq1)
 {
 	struct cmd_q	*cmdq = cmdq1->data;
 
+	if (cmdq1->client_exit >= 0)
+		cmdq->client_exit = cmdq1->client_exit;
+
 	cmdq_free(cmdq1);
 
 	cfg_references--;
diff --git a/cmd-split-window.c b/cmd-split-window.c
index 601dcb17..506c8033 100644
--- a/cmd-split-window.c
+++ b/cmd-split-window.c
@@ -18,7 +18,11 @@
 
 #include <sys/types.h>
 
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #include "tmux.h"
@@ -37,7 +41,6 @@ const struct cmd_entry cmd_split_window_entry = {
 	CMD_TARGET_PANE_USAGE " [command]",
 	0,
 	cmd_split_window_key_binding,
-	NULL,
 	cmd_split_window_exec
 };
 
@@ -58,16 +61,14 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
 	struct window		*w;
 	struct window_pane	*wp, *new_wp = NULL;
 	struct environ		 env;
-	const char		*cmd, *cwd, *shell;
-	char			*cause, *new_cause;
+	const char		*cmd, *shell, *template;
+	char			*cause, *new_cause, *cp;
 	u_int			 hlimit;
-	int			 size, percentage;
+	int			 size, percentage, cwd, fd = -1;
 	enum layout_type	 type;
 	struct layout_cell	*lc;
-	const char		*template;
 	struct client		*c;
 	struct format_tree	*ft;
-	char			*cp;
 
 	if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL)
 		return (CMD_RETURN_ERROR);
@@ -83,7 +84,29 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
 		cmd = options_get_string(&s->options, "default-command");
 	else
 		cmd = args->argv[0];
-	cwd = cmd_get_default_path(cmdq, args_get(args, 'c'));
+
+	if (args_has(args, 'c')) {
+		ft = format_create();
+		if ((c = cmd_find_client(cmdq, NULL, 1)) != NULL)
+			format_client(ft, c);
+		format_session(ft, s);
+		format_winlink(ft, s, s->curw);
+		format_window_pane(ft, s->curw->window->active);
+		cp = format_expand(ft, args_get(args, 'c'));
+		format_free(ft);
+
+		fd = open(cp, O_RDONLY|O_DIRECTORY);
+		free(cp);
+		if (fd == -1) {
+			cmdq_error(cmdq, "bad working directory: %s",
+			    strerror(errno));
+			return (CMD_RETURN_ERROR);
+		}
+		cwd = fd;
+	} else if (cmdq->client != NULL && cmdq->client->session == NULL)
+		cwd = cmdq->client->cwd;
+	else
+		cwd = s->cwd;
 
 	type = LAYOUT_TOPBOTTOM;
 	if (args_has(args, 'h'))
@@ -156,6 +179,9 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
 		format_free(ft);
 	}
 	notify_window_layout_changed(w);
+
+	if (fd != -1)
+		close(fd);
 	return (CMD_RETURN_NORMAL);
 
 error:
@@ -164,5 +190,7 @@ error:
 		window_remove_pane(w, new_wp);
 	cmdq_error(cmdq, "create pane failed: %s", cause);
 	free(cause);
+	if (fd != -1)
+		close(fd);
 	return (CMD_RETURN_ERROR);
 }
diff --git a/cmd-start-server.c b/cmd-start-server.c
index cba2403b..33b28b4a 100644
--- a/cmd-start-server.c
+++ b/cmd-start-server.c
@@ -32,7 +32,6 @@ const struct cmd_entry cmd_start_server_entry = {
 	"",
 	CMD_STARTSERVER,
 	NULL,
-	NULL,
 	cmd_start_server_exec
 };
 
diff --git a/cmd-string.c b/cmd-string.c
index 7e84eda6..e793ea02 100644
--- a/cmd-string.c
+++ b/cmd-string.c
@@ -318,10 +318,13 @@ cmd_string_expand_tilde(const char *s, size_t *p)
 {
 	struct passwd		*pw;
 	struct environ_entry	*envent;
-	char			*home, *path, *username;
+	char			*home, *path, *user, *cp;
+	int			 last;
 
 	home = NULL;
-	if (cmd_string_getc(s, p) == '/') {
+
+	last = cmd_string_getc(s, p);
+	if (last == EOF || last == '/' || last == ' '|| last == '\t') {
 		envent = environ_find(&global_environ, "HOME");
 		if (envent != NULL && *envent->value != '\0')
 			home = envent->value;
@@ -329,15 +332,27 @@ cmd_string_expand_tilde(const char *s, size_t *p)
 			home = pw->pw_dir;
 	} else {
 		cmd_string_ungetc(p);
-		if ((username = cmd_string_string(s, p, '/', 0)) == NULL)
-			return (NULL);
-		if ((pw = getpwnam(username)) != NULL)
+
+		cp = user = xmalloc(strlen(s));
+		for (;;) {
+			last = cmd_string_getc(s, p);
+			if (last == EOF || last == '/' || last == ' '|| last == '\t')
+				break;
+			*cp++ = last;
+		}
+		*cp = '\0';
+
+		if ((pw = getpwnam(user)) != NULL)
 			home = pw->pw_dir;
-		free(username);
+		free(user);
 	}
+
 	if (home == NULL)
 		return (NULL);
 
-	xasprintf(&path, "%s/", home);
+	if (last != EOF)
+		xasprintf(&path, "%s%c", home, last);
+	else
+		xasprintf(&path, "%s", home);
 	return (path);
 }
diff --git a/cmd-suspend-client.c b/cmd-suspend-client.c
index 101658b1..e0e375fc 100644
--- a/cmd-suspend-client.c
+++ b/cmd-suspend-client.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_suspend_client_entry = {
 	CMD_TARGET_CLIENT_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_suspend_client_exec
 };
 
diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c
index 05317260..b8ff7690 100644
--- a/cmd-swap-pane.c
+++ b/cmd-swap-pane.c
@@ -35,7 +35,6 @@ const struct cmd_entry cmd_swap_pane_entry = {
 	"[-dDU] " CMD_SRCDST_PANE_USAGE,
 	0,
 	cmd_swap_pane_key_binding,
-	NULL,
 	cmd_swap_pane_exec
 };
 
diff --git a/cmd-swap-window.c b/cmd-swap-window.c
index f9a2cb1b..1591d403 100644
--- a/cmd-swap-window.c
+++ b/cmd-swap-window.c
@@ -34,7 +34,6 @@ const struct cmd_entry cmd_swap_window_entry = {
 	"[-d] " CMD_SRCDST_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_swap_window_exec
 };
 
diff --git a/cmd-switch-client.c b/cmd-switch-client.c
index 9adb2146..d101c52b 100644
--- a/cmd-switch-client.c
+++ b/cmd-switch-client.c
@@ -36,7 +36,6 @@ const struct cmd_entry cmd_switch_client_entry = {
 	"[-lnpr] [-c target-client] [-t target-session]",
 	CMD_READONLY,
 	cmd_switch_client_key_binding,
-	NULL,
 	cmd_switch_client_exec
 };
 
diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c
index dc037dde..cf6ad506 100644
--- a/cmd-unbind-key.c
+++ b/cmd-unbind-key.c
@@ -26,7 +26,6 @@
  * Unbind key from command.
  */
 
-enum cmd_retval	 cmd_unbind_key_check(struct args *);
 enum cmd_retval	 cmd_unbind_key_exec(struct cmd *, struct cmd_q *);
 enum cmd_retval	 cmd_unbind_key_table(struct cmd *, struct cmd_q *, int);
 
@@ -36,20 +35,9 @@ const struct cmd_entry cmd_unbind_key_entry = {
 	"[-acn] [-t key-table] key",
 	0,
 	NULL,
-	cmd_unbind_key_check,
 	cmd_unbind_key_exec
 };
 
-enum cmd_retval
-cmd_unbind_key_check(struct args *args)
-{
-	if (args_has(args, 'a') && args->argc != 0)
-		return (CMD_RETURN_ERROR);
-	if (!args_has(args, 'a') && args->argc != 1)
-		return (CMD_RETURN_ERROR);
-	return (CMD_RETURN_NORMAL);
-}
-
 enum cmd_retval
 cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
 {
@@ -58,13 +46,22 @@ cmd_unbind_key_exec(struct cmd *self, struct cmd_q *cmdq)
 	int			 key;
 
 	if (!args_has(args, 'a')) {
+		if (args->argc != 1) {
+			cmdq_error(cmdq, "missing key");
+			return (CMD_RETURN_ERROR);
+		}
 		key = key_string_lookup_string(args->argv[0]);
 		if (key == KEYC_NONE) {
 			cmdq_error(cmdq, "unknown key: %s", args->argv[0]);
 			return (CMD_RETURN_ERROR);
 		}
-	} else
+	} else {
+		if (args->argc != 0) {
+			cmdq_error(cmdq, "key given with -a");
+			return (CMD_RETURN_ERROR);
+		}
 		key = KEYC_NONE;
+	}
 
 	if (args_has(args, 't'))
 		return (cmd_unbind_key_table(self, cmdq, key));
diff --git a/cmd-unlink-window.c b/cmd-unlink-window.c
index 39cdd8ed..ec69b91f 100644
--- a/cmd-unlink-window.c
+++ b/cmd-unlink-window.c
@@ -32,7 +32,6 @@ const struct cmd_entry cmd_unlink_window_entry = {
 	"[-k] " CMD_TARGET_WINDOW_USAGE,
 	0,
 	NULL,
-	NULL,
 	cmd_unlink_window_exec
 };
 
diff --git a/cmd-wait-for.c b/cmd-wait-for.c
index d40ba49e..e251863d 100644
--- a/cmd-wait-for.c
+++ b/cmd-wait-for.c
@@ -33,10 +33,9 @@ enum cmd_retval cmd_wait_for_exec(struct cmd *, struct cmd_q *);
 const struct cmd_entry cmd_wait_for_entry = {
 	"wait-for", "wait",
 	"LSU", 1, 1,
-	"[-LSU] channel",
+	"[-L|-S|-U] channel",
 	0,
 	NULL,
-	NULL,
 	cmd_wait_for_exec
 };
 
diff --git a/cmd.c b/cmd.c
index 282fb112..414c9067 100644
--- a/cmd.c
+++ b/cmd.c
@@ -254,8 +254,6 @@ cmd_parse(int argc, char **argv, const char *file, u_int line, char **cause)
 		goto usage;
 	if (entry->args_upper != -1 && args->argc > entry->args_upper)
 		goto usage;
-	if (entry->check != NULL && entry->check(args) != 0)
-		goto usage;
 
 	cmd = xcalloc(1, sizeof *cmd);
 	cmd->entry = entry;
@@ -315,7 +313,6 @@ cmd_print(struct cmd *cmd, char *buf, size_t len)
 struct session *
 cmd_current_session(struct cmd_q *cmdq, int prefer_unattached)
 {
-	struct msg_command_data	*data = cmdq->msgdata;
 	struct client		*c = cmdq->client;
 	struct session		*s;
 	struct sessionslist	 ss;
@@ -357,13 +354,6 @@ cmd_current_session(struct cmd_q *cmdq, int prefer_unattached)
 			return (s);
 	}
 
-	/* Use the session from the TMUX environment variable. */
-	if (data != NULL && data->pid == getpid() && data->session_id != -1) {
-		s = session_find_by_id(data->session_id);
-		if (s != NULL)
-			return (s);
-	}
-
 	return (cmd_choose_session(prefer_unattached));
 }
 
@@ -1278,87 +1268,3 @@ cmd_template_replace(const char *template, const char *s, int idx)
 
 	return (buf);
 }
-
-/*
- * Return the default path for a new pane, using the given path or the
- * default-path option if it is NULL. Several special values are accepted: the
- * empty string or relative path for the current pane's working directory, ~
- * for the user's home, - for the session working directory, . for the tmux
- * server's working directory. The default on failure is the session's working
- * directory.
- */
-const char *
-cmd_get_default_path(struct cmd_q *cmdq, const char *cwd)
-{
-	struct client		*c = cmdq->client;
-	struct session		*s;
-	struct environ_entry	*envent;
-	const char		*root;
-	char			 tmp[MAXPATHLEN];
-	struct passwd		*pw;
-	int			 n;
-	size_t			 skip;
-	static char		 path[MAXPATHLEN];
-
-	if ((s = cmd_current_session(cmdq, 0)) == NULL)
-		return (NULL);
-
-	if (cwd == NULL)
-		cwd = options_get_string(&s->options, "default-path");
-
-	skip = 1;
-	if (strcmp(cwd, "$HOME") == 0 || strncmp(cwd, "$HOME/", 6) == 0) {
-		/* User's home directory - $HOME. */
-		skip = 5;
-		goto find_home;
-	} else if (cwd[0] == '~' && (cwd[1] == '\0' || cwd[1] == '/')) {
-		/* User's home directory - ~. */
-		goto find_home;
-	} else if (cwd[0] == '-' && (cwd[1] == '\0' || cwd[1] == '/')) {
-		/* Session working directory. */
-		root = s->cwd;
-		goto complete_path;
-	} else if (cwd[0] == '.' && (cwd[1] == '\0' || cwd[1] == '/')) {
-		/* Server working directory. */
-		if (getcwd(tmp, sizeof tmp) != NULL) {
-			root = tmp;
-			goto complete_path;
-		}
-		return (s->cwd);
-	} else if (*cwd == '/') {
-		/* Absolute path. */
-		return (cwd);
-	} else {
-		/* Empty or relative path. */
-		if (c != NULL && c->session == NULL && c->cwd != NULL)
-			root = c->cwd;
-		else if (s->curw != NULL)
-			root = osdep_get_cwd(s->curw->window->active->fd);
-		else
-			return (s->cwd);
-		skip = 0;
-		if (root != NULL)
-			goto complete_path;
-	}
-
-	return (s->cwd);
-
-find_home:
-	envent = environ_find(&global_environ, "HOME");
-	if (envent != NULL && *envent->value != '\0')
-		root = envent->value;
-	else if ((pw = getpwuid(getuid())) != NULL)
-		root = pw->pw_dir;
-	else
-		return (s->cwd);
-
-complete_path:
-	if (root[skip] == '\0') {
-		strlcpy(path, root, sizeof path);
-		return (path);
-	}
-	n = snprintf(path, sizeof path, "%s/%s", root, cwd + skip);
-	if (n > 0 && (size_t)n < sizeof path)
-		return (path);
-	return (s->cwd);
-}
diff --git a/control.c b/control.c
index aa79085a..52fdb524 100644
--- a/control.c
+++ b/control.c
@@ -73,9 +73,9 @@ control_callback(struct client *c, int closed, unused void *data)
 			c->cmdq->time = time(NULL);
 			c->cmdq->number++;
 
-			cmdq_guard(c->cmdq, "begin");
+			cmdq_guard(c->cmdq, "begin", 1);
 			control_write(c, "parse error: %s", cause);
-			cmdq_guard(c->cmdq, "error");
+			cmdq_guard(c->cmdq, "error", 1);
 
 			free(cause);
 		} else {
diff --git a/format.c b/format.c
index 8c4c7842..d8d34fe7 100644
--- a/format.c
+++ b/format.c
@@ -18,6 +18,8 @@
 
 #include <sys/types.h>
 
+#include <ctype.h>
+#include <errno.h>
 #include <netdb.h>
 #include <stdarg.h>
 #include <stdlib.h>
@@ -32,9 +34,10 @@
  * string.
  */
 
-int	format_replace(struct format_tree *, const char *, size_t, char **,
-	    size_t *, size_t *);
-void	format_window_pane_tabs(struct format_tree *, struct window_pane *);
+int	 format_replace(struct format_tree *, const char *, size_t, char **,
+	     size_t *, size_t *);
+char	*format_get_command(struct window_pane *);
+void	 format_window_pane_tabs(struct format_tree *, struct window_pane *);
 
 /* Format key-value replacement entry. */
 RB_GENERATE(format_tree, format_entry, entry, format_cmp);
@@ -118,7 +121,7 @@ format_create(void)
 
 	if (gethostname(host, sizeof host) == 0) {
 		format_add(ft, "host", "%s", host);
-		if ((ptr = strrchr(host, '.')) != NULL)
+		if ((ptr = strchr(host, '.')) != NULL)
 			*ptr = '\0';
 		format_add(ft, "host_short", "%s", host);
 	}
@@ -151,6 +154,7 @@ void
 format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
 {
 	struct format_entry	*fe;
+	struct format_entry	*fe_now;
 	va_list			 ap;
 
 	fe = xmalloc(sizeof *fe);
@@ -160,7 +164,13 @@ format_add(struct format_tree *ft, const char *key, const char *fmt, ...)
 	xvasprintf(&fe->value, fmt, ap);
 	va_end(ap);
 
-	RB_INSERT(format_tree, ft, fe);
+	fe_now = RB_INSERT(format_tree, ft, fe);
+	if (fe_now != NULL) {
+		free(fe_now->value);
+		fe_now->value = fe->value;
+		free(fe->key);
+		free(fe);
+	}
 }
 
 /* Find a format entry. */
@@ -181,18 +191,40 @@ format_find(struct format_tree *ft, const char *key)
  * #{?blah,a,b} is replace with a if blah exists and is nonzero else b.
  */
 int
-format_replace(struct format_tree *ft,
-    const char *key, size_t keylen, char **buf, size_t *len, size_t *off)
+format_replace(struct format_tree *ft, const char *key, size_t keylen,
+    char **buf, size_t *len, size_t *off)
 {
-	char		*copy, *ptr;
+	char		*copy, *copy0, *endptr, *ptr, *saved;
 	const char	*value;
 	size_t		 valuelen;
+	u_long		 limit = ULONG_MAX;
 
 	/* Make a copy of the key. */
-	copy = xmalloc(keylen + 1);
+	copy0 = copy = xmalloc(keylen + 1);
 	memcpy(copy, key, keylen);
 	copy[keylen] = '\0';
 
+	/* Is there a length limit or whatnot? */
+	if (!islower((u_char) *copy) && *copy != '?') {
+		while (*copy != ':' && *copy != '\0') {
+			switch (*copy) {
+			case '=':
+				errno = 0;
+				limit = strtoul(copy + 1, &endptr, 10);
+				if (errno == ERANGE && limit == ULONG_MAX)
+					goto fail;
+				copy = endptr;
+				break;
+			default:
+				copy++;
+				break;
+			}
+		}
+		if (*copy != ':')
+			goto fail;
+		copy++;
+	}
+
 	/*
 	 * Is this a conditional? If so, check it exists and extract either the
 	 * first or second element. If not, look up the key directly.
@@ -216,13 +248,20 @@ format_replace(struct format_tree *ft,
 				goto fail;
 			value = ptr + 1;
 		}
+		saved = format_expand(ft, value);
+		value = saved;
 	} else {
 		value = format_find(ft, copy);
 		if (value == NULL)
 			value = "";
+		saved = NULL;
 	}
 	valuelen = strlen(value);
 
+	/* Truncate the value if needed. */
+	if (valuelen > limit)
+		valuelen = limit;
+
 	/* Expand the buffer and copy in the value. */
 	while (*len - *off < valuelen + 1) {
 		*buf = xrealloc(*buf, 2, *len);
@@ -231,11 +270,12 @@ format_replace(struct format_tree *ft,
 	memcpy(*buf + *off, value, valuelen);
 	*off += valuelen;
 
-	free(copy);
+	free(saved);
+	free(copy0);
 	return (0);
 
 fail:
-	free(copy);
+	free(copy0);
 	return (-1);
 }
 
@@ -243,10 +283,10 @@ fail:
 char *
 format_expand(struct format_tree *ft, const char *fmt)
 {
-	char		*buf, *ptr;
-	const char	*s;
+	char		*buf;
+	const char	*ptr, *s;
 	size_t		 off, len, n;
-	int     	 ch;
+	int     	 ch, brackets;
 
 	len = 64;
 	buf = xmalloc(len);
@@ -264,11 +304,16 @@ format_expand(struct format_tree *ft, const char *fmt)
 		fmt++;
 
 		ch = (u_char) *fmt++;
-
 		switch (ch) {
 		case '{':
-			ptr = strchr(fmt, '}');
-			if (ptr == NULL)
+			brackets = 1;
+			for (ptr = fmt; *ptr != '\0'; ptr++) {
+				if (*ptr == '{')
+					brackets++;
+				if (*ptr == '}' && --brackets == 0)
+					break;
+			}
+			if (*ptr != '}' || brackets != 0)
 				break;
 			n = ptr - fmt;
 
@@ -304,6 +349,26 @@ format_expand(struct format_tree *ft, const char *fmt)
 	return (buf);
 }
 
+/* Get command name for format. */
+char *
+format_get_command(struct window_pane *wp)
+{
+	char	*cmd, *out;
+
+	cmd = get_proc_name(wp->fd, wp->tty);
+	if (cmd == NULL || *cmd == '\0') {
+		free(cmd);
+		cmd = xstrdup(wp->cmd);
+		if (cmd == NULL || *cmd == '\0') {
+			free(cmd);
+			cmd = xstrdup(wp->shell);
+		}
+	}
+	out = parse_window_name(cmd);
+	free(cmd);
+	return (out);
+}
+
 /* Set default format keys for a session. */
 void
 format_session(struct format_tree *ft, struct session *s)
@@ -343,11 +408,12 @@ format_client(struct format_tree *ft, struct client *c)
 	time_t		 t;
 	struct session	*s;
 
-	format_add(ft, "client_cwd", "%s", c->cwd);
 	format_add(ft, "client_height", "%u", c->tty.sy);
 	format_add(ft, "client_width", "%u", c->tty.sx);
-	format_add(ft, "client_tty", "%s", c->tty.path);
-	format_add(ft, "client_termname", "%s", c->tty.termname);
+	if (c->tty.path != NULL)
+		format_add(ft, "client_tty", "%s", c->tty.path);
+	if (c->tty.termname != NULL)
+		format_add(ft, "client_termname", "%s", c->tty.termname);
 
 	t = c->creation_time.tv_sec;
 	format_add(ft, "client_created", "%lld", (long long) t);
@@ -381,28 +447,50 @@ format_client(struct format_tree *ft, struct client *c)
 		format_add(ft, "client_last_session", "%s", s->name);
 }
 
+/* Set default format keys for a window. */
+void
+format_window(struct format_tree *ft, struct window *w)
+{
+	char	*layout;
+
+	layout = layout_dump(w);
+
+	format_add(ft, "window_id", "@%u", w->id);
+	format_add(ft, "window_name", "%s", w->name);
+	format_add(ft, "window_width", "%u", w->sx);
+	format_add(ft, "window_height", "%u", w->sy);
+	format_add(ft, "window_layout", "%s", layout);
+	format_add(ft, "window_panes", "%u", window_count_panes(w));
+
+	free(layout);
+}
+
 /* Set default format keys for a winlink. */
 void
 format_winlink(struct format_tree *ft, struct session *s, struct winlink *wl)
 {
 	struct window	*w = wl->window;
-	char		*layout, *flags;
+	char		*flags;
 
-	layout = layout_dump(w);
 	flags = window_printable_flags(s, wl);
 
-	format_add(ft, "window_id", "@%u", w->id);
+	format_window(ft, w);
+
 	format_add(ft, "window_index", "%d", wl->idx);
-	format_add(ft, "window_name", "%s", w->name);
-	format_add(ft, "window_width", "%u", w->sx);
-	format_add(ft, "window_height", "%u", w->sy);
 	format_add(ft, "window_flags", "%s", flags);
-	format_add(ft, "window_layout", "%s", layout);
 	format_add(ft, "window_active", "%d", wl == s->curw);
-	format_add(ft, "window_panes", "%u", window_count_panes(w));
+
+	format_add(ft, "window_bell_flag", "%u",
+	    !!(wl->flags & WINLINK_BELL));
+	format_add(ft, "window_content_flag", "%u",
+	    !!(wl->flags & WINLINK_CONTENT));
+	format_add(ft, "window_activity_flag", "%u",
+	    !!(wl->flags & WINLINK_ACTIVITY));
+	format_add(ft, "window_silence_flag", "%u",
+	    !!(wl->flags & WINLINK_SILENCE));
+
 
 	free(flags);
-	free(layout);
 }
 
 /* Add window pane tabs. */
@@ -435,7 +523,6 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp)
 	struct grid_line	*gl;
 	unsigned long long	 size;
 	u_int			 i, idx;
-	const char		*cwd;
 	char			*cmd;
 
 	size = 0;
@@ -468,11 +555,7 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp)
 	format_add(ft, "pane_pid", "%ld", (long) wp->pid);
 	if (wp->cmd != NULL)
 		format_add(ft, "pane_start_command", "%s", wp->cmd);
-	if (wp->cwd != NULL)
-		format_add(ft, "pane_start_path", "%s", wp->cwd);
-	if ((cwd = osdep_get_cwd(wp->fd)) != NULL)
-		format_add(ft, "pane_current_path", "%s", cwd);
-	if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) {
+	if ((cmd = format_get_command(wp)) != NULL) {
 		format_add(ft, "pane_current_command", "%s", cmd);
 		free(cmd);
 	}
diff --git a/grid.c b/grid.c
index 2955e8ba..9e800243 100644
--- a/grid.c
+++ b/grid.c
@@ -268,8 +268,7 @@ grid_get_cell(struct grid *gd, u_int px, u_int py)
 
 /* Set cell at relative position. */
 void
-grid_set_cell(
-    struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
+grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
 {
 	if (grid_check_y(gd, py) != 0)
 		return;
@@ -592,6 +591,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
 	char			*buf, code[128];
 	size_t			 len, off, size, codelen;
 	u_int			 xx;
+	const struct grid_line	*gl;
 
 	GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
 
@@ -604,8 +604,11 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
 	buf = xmalloc(len);
 	off = 0;
 
+	gl = grid_peek_line(gd, py);
 	for (xx = px; xx < px + nx; xx++) {
-		gc = grid_peek_cell(gd, xx, py);
+		if (gl == NULL || xx >= gl->cellsize)
+			break;
+		gc = &gl->celldata[xx];
 		if (gc->flags & GRID_FLAG_PADDING)
 			continue;
 		grid_cell_get(gc, &ud);
@@ -653,8 +656,8 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
  * available.
  */
 void
-grid_duplicate_lines(
-    struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny)
+grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
+    u_int ny)
 {
 	struct grid_line	*dstl, *srcl;
 	u_int			 yy;
diff --git a/input.c b/input.c
index 30d3bb98..259fad16 100644
--- a/input.c
+++ b/input.c
@@ -70,6 +70,10 @@ int	input_input(struct input_ctx *);
 int	input_c0_dispatch(struct input_ctx *);
 int	input_esc_dispatch(struct input_ctx *);
 int	input_csi_dispatch(struct input_ctx *);
+void	input_csi_dispatch_rm(struct input_ctx *);
+void	input_csi_dispatch_rm_private(struct input_ctx *);
+void	input_csi_dispatch_sm(struct input_ctx *);
+void	input_csi_dispatch_sm_private(struct input_ctx *);
 void	input_csi_dispatch_sgr(struct input_ctx *);
 int	input_dcs_dispatch(struct input_ctx *);
 int	input_utf8_open(struct input_ctx *);
@@ -1071,7 +1075,6 @@ int
 input_csi_dispatch(struct input_ctx *ictx)
 {
 	struct screen_write_ctx	       *sctx = &ictx->ctx;
-	struct window_pane	       *wp = ictx->wp;
 	struct screen		       *s = sctx->s;
 	struct input_table_entry       *entry;
 	int			 	n, m;
@@ -1230,7 +1233,60 @@ input_csi_dispatch(struct input_ctx *ictx)
 		screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy);
 		break;
 	case INPUT_CSI_RM:
-		switch (input_get(ictx, 0, 0, -1)) {
+		input_csi_dispatch_rm(ictx);
+		break;
+	case INPUT_CSI_RM_PRIVATE:
+		input_csi_dispatch_rm_private(ictx);
+		break;
+	case INPUT_CSI_SCP:
+		memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
+		ictx->old_cx = s->cx;
+		ictx->old_cy = s->cy;
+		break;
+	case INPUT_CSI_SGR:
+		input_csi_dispatch_sgr(ictx);
+		break;
+	case INPUT_CSI_SM:
+		input_csi_dispatch_sm(ictx);
+		break;
+	case INPUT_CSI_SM_PRIVATE:
+		input_csi_dispatch_sm_private(ictx);
+		break;
+	case INPUT_CSI_TBC:
+		switch (input_get(ictx, 0, 0, 0)) {
+		case 0:
+			if (s->cx < screen_size_x(s))
+				bit_clear(s->tabs, s->cx);
+			break;
+		case 3:
+			bit_nclear(s->tabs, 0, screen_size_x(s) - 1);
+			break;
+		default:
+			log_debug("%s: unknown '%c'", __func__, ictx->ch);
+			break;
+		}
+		break;
+	case INPUT_CSI_VPA:
+		n = input_get(ictx, 0, 1, 1);
+		screen_write_cursormove(sctx, s->cx, n - 1);
+		break;
+	case INPUT_CSI_DECSCUSR:
+		n = input_get(ictx, 0, 0, 0);
+		screen_set_cursor_style(s, n);
+		break;
+	}
+
+	return (0);
+}
+
+/* Handle CSI RM. */
+void
+input_csi_dispatch_rm(struct input_ctx *ictx)
+{
+	u_int	i;
+
+	for (i = 0; i < ictx->param_list_len; i++) {
+		switch (input_get(ictx, i, 0, -1)) {
 		case 4:		/* IRM */
 			screen_write_mode_clear(&ictx->ctx, MODE_INSERT);
 			break;
@@ -1238,10 +1294,18 @@ input_csi_dispatch(struct input_ctx *ictx)
 			log_debug("%s: unknown '%c'", __func__, ictx->ch);
 			break;
 		}
-		break;
-	case INPUT_CSI_RM_PRIVATE:
-		switch (input_get(ictx, 0, 0, -1)) {
-		case 1:		/* GATM */
+	}
+}
+
+/* Handle CSI private RM. */
+void
+input_csi_dispatch_rm_private(struct input_ctx *ictx)
+{
+	u_int	i;
+
+	for (i = 0; i < ictx->param_list_len; i++) {
+		switch (input_get(ictx, i, 0, -1)) {
+		case 1:		/* DECCKM */
 			screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR);
 			break;
 		case 3:		/* DECCOLM */
@@ -1271,10 +1335,10 @@ input_csi_dispatch(struct input_ctx *ictx)
 			break;
 		case 47:
 		case 1047:
-			window_pane_alternate_off(wp, &ictx->cell, 0);
+			window_pane_alternate_off(ictx->wp, &ictx->cell, 0);
 			break;
 		case 1049:
-			window_pane_alternate_off(wp, &ictx->cell, 1);
+			window_pane_alternate_off(ictx->wp, &ictx->cell, 1);
 			break;
 		case 2004:
 			screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE);
@@ -1283,17 +1347,17 @@ input_csi_dispatch(struct input_ctx *ictx)
 			log_debug("%s: unknown '%c'", __func__, ictx->ch);
 			break;
 		}
-		break;
-	case INPUT_CSI_SCP:
-		memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell);
-		ictx->old_cx = s->cx;
-		ictx->old_cy = s->cy;
-		break;
-	case INPUT_CSI_SGR:
-		input_csi_dispatch_sgr(ictx);
-		break;
-	case INPUT_CSI_SM:
-		switch (input_get(ictx, 0, 0, -1)) {
+	}
+}
+
+/* Handle CSI SM. */
+void
+input_csi_dispatch_sm(struct input_ctx *ictx)
+{
+	u_int	i;
+
+	for (i = 0; i < ictx->param_list_len; i++) {
+		switch (input_get(ictx, i, 0, -1)) {
 		case 4:		/* IRM */
 			screen_write_mode_set(&ictx->ctx, MODE_INSERT);
 			break;
@@ -1301,10 +1365,18 @@ input_csi_dispatch(struct input_ctx *ictx)
 			log_debug("%s: unknown '%c'", __func__, ictx->ch);
 			break;
 		}
-		break;
-	case INPUT_CSI_SM_PRIVATE:
-		switch (input_get(ictx, 0, 0, -1)) {
-		case 1:		/* GATM */
+	}
+}
+
+/* Handle CSI private SM. */
+void
+input_csi_dispatch_sm_private(struct input_ctx *ictx)
+{
+	u_int	i;
+
+	for (i = 0; i < ictx->param_list_len; i++) {
+		switch (input_get(ictx, i, 0, -1)) {
+		case 1:		/* DECCKM */
 			screen_write_mode_set(&ictx->ctx, MODE_KCURSOR);
 			break;
 		case 3:		/* DECCOLM */
@@ -1330,10 +1402,10 @@ input_csi_dispatch(struct input_ctx *ictx)
 			screen_write_mode_set(&ictx->ctx, MODE_MOUSE_ANY);
 			break;
 		case 1004:
-			if (s->mode & MODE_FOCUSON)
+			if (ictx->ctx.s->mode & MODE_FOCUSON)
 				break;
 			screen_write_mode_set(&ictx->ctx, MODE_FOCUSON);
-			wp->flags |= PANE_FOCUSPUSH; /* force update */
+			ictx->wp->flags |= PANE_FOCUSPUSH; /* force update */
 			break;
 		case 1005:
 			screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8);
@@ -1343,10 +1415,10 @@ input_csi_dispatch(struct input_ctx *ictx)
 			break;
 		case 47:
 		case 1047:
-			window_pane_alternate_on(wp, &ictx->cell, 0);
+			window_pane_alternate_on(ictx->wp, &ictx->cell, 0);
 			break;
 		case 1049:
-			window_pane_alternate_on(wp, &ictx->cell, 1);
+			window_pane_alternate_on(ictx->wp, &ictx->cell, 1);
 			break;
 		case 2004:
 			screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE);
@@ -1355,32 +1427,7 @@ input_csi_dispatch(struct input_ctx *ictx)
 			log_debug("%s: unknown '%c'", __func__, ictx->ch);
 			break;
 		}
-		break;
-	case INPUT_CSI_TBC:
-		switch (input_get(ictx, 0, 0, 0)) {
-		case 0:
-			if (s->cx < screen_size_x(s))
-				bit_clear(s->tabs, s->cx);
-			break;
-		case 3:
-			bit_nclear(s->tabs, 0, screen_size_x(s) - 1);
-			break;
-		default:
-			log_debug("%s: unknown '%c'", __func__, ictx->ch);
-			break;
-		}
-		break;
-	case INPUT_CSI_VPA:
-		n = input_get(ictx, 0, 1, 1);
-		screen_write_cursormove(sctx, s->cx, n - 1);
-		break;
-	case INPUT_CSI_DECSCUSR:
-		n = input_get(ictx, 0, 0, 0);
-		screen_set_cursor_style(s, n);
-		break;
 	}
-
-	return (0);
 }
 
 /* Handle CSI SGR. */
diff --git a/job.c b/job.c
index b2c2251c..d7bd852b 100644
--- a/job.c
+++ b/job.c
@@ -144,7 +144,7 @@ job_write_callback(unused struct bufferevent *bufev, void *data)
 	size_t		 len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event));
 
 	log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd,
-		    (long) job->pid, len);
+	    (long) job->pid, len);
 
 	if (len == 0) {
 		shutdown(job->fd, SHUT_WR);
diff --git a/layout.c b/layout.c
index b74bd789..646d1bd9 100644
--- a/layout.c
+++ b/layout.c
@@ -533,6 +533,9 @@ layout_resize_pane_mouse(struct client *c)
 	pane_border = 0;
 	if (m->event & MOUSE_EVENT_DRAG && m->flags & MOUSE_RESIZE_PANE) {
 		TAILQ_FOREACH(wp, &w->panes, entry) {
+			if (!window_pane_visible(wp))
+				continue;
+
 			if (wp->xoff + wp->sx == m->lx &&
 			    wp->yoff <= 1 + m->ly &&
 			    wp->yoff + wp->sy >= m->ly) {
@@ -550,7 +553,7 @@ layout_resize_pane_mouse(struct client *c)
 		}
 		if (pane_border)
 			server_redraw_window(w);
-	} else if (~m->event & MOUSE_EVENT_UP) {
+	} else if (m->event & MOUSE_EVENT_DOWN) {
 		TAILQ_FOREACH(wp, &w->panes, entry) {
 			if ((wp->xoff + wp->sx == m->x &&
 			    wp->yoff <= 1 + m->y &&
diff --git a/names.c b/names.c
index f536d2fc..7c02961c 100644
--- a/names.c
+++ b/names.c
@@ -22,12 +22,10 @@
 #include <libgen.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 
 #include "tmux.h"
 
 void	 window_name_callback(unused int, unused short, void *);
-char	*parse_window_name(const char *);
 
 void
 queue_window_name(struct window *w)
@@ -47,7 +45,7 @@ void
 window_name_callback(unused int fd, unused short events, void *data)
 {
 	struct window	*w = data;
-	char		*name, *wname;
+	char		*name;
 
 	if (w->active == NULL)
 		return;
@@ -59,49 +57,39 @@ window_name_callback(unused int fd, unused short events, void *data)
 	}
 	queue_window_name(w);
 
-	if (w->active->screen != &w->active->base)
-		name = NULL;
-	else
-		name = osdep_get_name(w->active->fd, w->active->tty);
-	if (name == NULL)
-		wname = default_window_name(w);
-	else {
-		/*
-		 * If tmux is using the default command, it will be a login
-		 * shell and argv[0] may have a - prefix. Remove this if it is
-		 * present. Ick.
-		 */
-		if (w->active->cmd != NULL && *w->active->cmd == '\0' &&
-		    name != NULL && name[0] == '-' && name[1] != '\0')
-			wname = parse_window_name(name + 1);
-		else
-			wname = parse_window_name(name);
-		free(name);
-	}
-
-	if (w->active->fd == -1) {
-		xasprintf(&name, "%s[dead]", wname);
-		free(wname);
-		wname = name;
-	}
-
-	if (strcmp(wname, w->name)) {
-		window_set_name(w, wname);
+	name = format_window_name(w);
+	if (strcmp(name, w->name) != 0) {
+		window_set_name(w, name);
 		server_status_window(w);
 	}
-	free(wname);
+	free(name);
 }
 
 char *
 default_window_name(struct window *w)
 {
-	if (w->active->screen != &w->active->base)
-		return (xstrdup("[tmux]"));
 	if (w->active->cmd != NULL && *w->active->cmd != '\0')
 		return (parse_window_name(w->active->cmd));
 	return (parse_window_name(w->active->shell));
 }
 
+char *
+format_window_name(struct window *w)
+{
+	struct format_tree	*ft;
+	char			*fmt, *name;
+
+	ft = format_create();
+	format_window(ft, w);
+	format_window_pane(ft, w->active);
+
+	fmt = options_get_string(&w->options, "automatic-rename-format");
+	name = format_expand(ft, fmt);
+
+	format_free(ft);
+	return (name);
+}
+
 char *
 parse_window_name(const char *in)
 {
@@ -111,7 +99,7 @@ parse_window_name(const char *in)
 	if (strncmp(name, "exec ", (sizeof "exec ") - 1) == 0)
 		name = name + (sizeof "exec ") - 1;
 
-	while (*name == ' ')
+	while (*name == ' ' || *name == '-')
 		name++;
 	if ((ptr = strchr(name, ' ')) != NULL)
 		*ptr = '\0';
diff --git a/options-table.c b/options-table.c
index 2281d652..5da095b1 100644
--- a/options-table.c
+++ b/options-table.c
@@ -125,11 +125,6 @@ const struct options_table_entry session_options_table[] = {
 	  .default_str = ""
 	},
 
-	{ .name = "default-path",
-	  .type = OPTIONS_TABLE_STRING,
-	  .default_str = ""
-	},
-
 	{ .name = "default-shell",
 	  .type = OPTIONS_TABLE_STRING,
 	  .default_str = _PATH_BSHELL
@@ -386,7 +381,7 @@ const struct options_table_entry session_options_table[] = {
 
 	{ .name = "status-right",
 	  .type = OPTIONS_TABLE_STRING,
-	  .default_str = "\"#22T\" %H:%M %d-%b-%y"
+	  .default_str = "\"#{=22:pane_title}\" %H:%M %d-%b-%y"
 	},
 
 	{ .name = "status-right-attr",
@@ -481,6 +476,11 @@ const struct options_table_entry window_options_table[] = {
 	  .default_num = 1
 	},
 
+	{ .name = "automatic-rename-format",
+	  .type = OPTIONS_TABLE_STRING,
+	  .default_str = "#{?pane_in_mode,[tmux],#{pane_current_command}}#{?pane_dead,[dead],}"
+	},
+
 	{ .name = "c0-change-trigger",
 	  .type = OPTIONS_TABLE_NUMBER,
 	  .default_num = 250,
diff --git a/osdep-openbsd.c b/osdep-openbsd.c
index 7be38a91..c61d90e7 100644
--- a/osdep-openbsd.c
+++ b/osdep-openbsd.c
@@ -37,9 +37,7 @@
 	((p)->p_stat == SSTOP || (p)->p_stat == SZOMB || (p)->p_stat == SDEAD)
 
 struct kinfo_proc	*cmp_procs(struct kinfo_proc *, struct kinfo_proc *);
-char			*osdep_get_name(int, char *);
-char			*osdep_get_cwd(int);
-struct event_base	*osdep_event_init(void);
+char			*get_proc_name(int, char *);
 
 struct kinfo_proc *
 cmp_procs(struct kinfo_proc *p1, struct kinfo_proc *p2)
@@ -134,23 +132,3 @@ error:
 	free(buf);
 	return (NULL);
 }
-
-char*
-osdep_get_cwd(int fd)
-{
-	int		name[] = { CTL_KERN, KERN_PROC_CWD, 0 };
-	static char	path[MAXPATHLEN];
-	size_t		pathlen = sizeof path;
-
-	if ((name[2] = tcgetpgrp(fd)) == -1)
-		return (NULL);
-	if (sysctl(name, 3, path, &pathlen, NULL, 0) != 0)
-		return (NULL);
-	return (path);
-}
-
-struct event_base *
-osdep_event_init(void)
-{
-	return (event_init());
-}
diff --git a/resize.c b/resize.c
index 5c365dfe..8d0bd275 100644
--- a/resize.c
+++ b/resize.c
@@ -92,7 +92,7 @@ recalculate_sizes(void)
 
 	for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
 		w = ARRAY_ITEM(&windows, i);
-		if (w == NULL)
+		if (w == NULL || w->active == NULL)
 			continue;
 		flag = options_get_number(&w->options, "aggressive-resize");
 
diff --git a/server-client.c b/server-client.c
index 5923eb0e..cc18a96f 100644
--- a/server-client.c
+++ b/server-client.c
@@ -40,9 +40,8 @@ void	server_client_reset_state(struct client *);
 int	server_client_assume_paste(struct session *);
 
 int	server_client_msg_dispatch(struct client *);
-void	server_client_msg_command(struct client *, struct msg_command_data *);
-void	server_client_msg_identify(
-	    struct client *, struct msg_identify_data *, int);
+void	server_client_msg_command(struct client *, struct imsg *);
+void	server_client_msg_identify(struct client *, struct imsg *);
 void	server_client_msg_shell(struct client *);
 
 /* Create a new client. */
@@ -63,6 +62,8 @@ server_client_create(int fd)
 		fatal("gettimeofday failed");
 	memcpy(&c->activity_time, &c->creation_time, sizeof c->activity_time);
 
+	environ_init(&c->environ);
+
 	c->cmdq = cmdq_new(c);
 	c->cmdq->client_exit = 1;
 
@@ -151,6 +152,8 @@ server_client_lost(struct client *c)
 	 */
 	if (c->flags & CLIENT_TERMINAL)
 		tty_free(&c->tty);
+	free(c->ttyname);
+	free(c->term);
 
 	evbuffer_free (c->stdin_data);
 	evbuffer_free (c->stdout_data);
@@ -162,6 +165,7 @@ server_client_lost(struct client *c)
 	screen_free(&c->status);
 
 	free(c->title);
+	close(c->cwd);
 
 	evtimer_del(&c->repeat_timer);
 
@@ -179,7 +183,6 @@ server_client_lost(struct client *c)
 
 	free(c->prompt_string);
 	free(c->prompt_buffer);
-	free(c->cwd);
 
 	c->cmdq->dead = 1;
 	cmdq_free(c->cmdq);
@@ -695,8 +698,6 @@ server_client_repeat_timer(unused int fd, unused short events, void *data)
 void
 server_client_check_exit(struct client *c)
 {
-	struct msg_exit_data	exitdata;
-
 	if (!(c->flags & CLIENT_EXIT))
 		return;
 
@@ -707,9 +708,7 @@ server_client_check_exit(struct client *c)
 	if (EVBUFFER_LENGTH(c->stderr_data) != 0)
 		return;
 
-	exitdata.retcode = c->retcode;
-	server_write_client(c, MSG_EXIT, &exitdata, sizeof exitdata);
-
+	server_write_client(c, MSG_EXIT, &c->retval, sizeof c->retval);
 	c->flags &= ~CLIENT_EXIT;
 }
 
@@ -790,10 +789,8 @@ int
 server_client_msg_dispatch(struct client *c)
 {
 	struct imsg		 imsg;
-	struct msg_command_data	 commanddata;
-	struct msg_identify_data identifydata;
-	struct msg_environ_data	 environdata;
 	struct msg_stdin_data	 stdindata;
+	const char		*data;
 	ssize_t			 n, datalen;
 
 	if ((n = imsg_read(&c->ibuf)) == -1 || n == 0)
@@ -804,40 +801,37 @@ server_client_msg_dispatch(struct client *c)
 			return (-1);
 		if (n == 0)
 			return (0);
+
+		data = imsg.data;
 		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
 
 		if (imsg.hdr.peerid != PROTOCOL_VERSION) {
 			server_write_client(c, MSG_VERSION, NULL, 0);
 			c->flags |= CLIENT_BAD;
+			if (imsg.fd != -1)
+				close(imsg.fd);
 			imsg_free(&imsg);
 			continue;
 		}
 
 		log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd);
 		switch (imsg.hdr.type) {
-		case MSG_COMMAND:
-			if (datalen != sizeof commanddata)
-				fatalx("bad MSG_COMMAND size");
-			memcpy(&commanddata, imsg.data, sizeof commanddata);
-
-			server_client_msg_command(c, &commanddata);
+		case MSG_IDENTIFY_FLAGS:
+		case MSG_IDENTIFY_TERM:
+		case MSG_IDENTIFY_TTYNAME:
+		case MSG_IDENTIFY_CWD:
+		case MSG_IDENTIFY_STDIN:
+		case MSG_IDENTIFY_ENVIRON:
+		case MSG_IDENTIFY_DONE:
+			server_client_msg_identify(c, &imsg);
 			break;
-		case MSG_IDENTIFY:
-			if (datalen != sizeof identifydata)
-				fatalx("bad MSG_IDENTIFY size");
-			memcpy(&identifydata, imsg.data, sizeof identifydata);
-#ifdef __CYGWIN__
-			imsg.fd = open(identifydata.ttyname, O_RDWR|O_NOCTTY);
-#endif
-			if (imsg.fd == -1)
-				fatalx("MSG_IDENTIFY missing fd");
-
-			server_client_msg_identify(c, &identifydata, imsg.fd);
+		case MSG_COMMAND:
+			server_client_msg_command(c, &imsg);
 			break;
 		case MSG_STDIN:
 			if (datalen != sizeof stdindata)
 				fatalx("bad MSG_STDIN size");
-			memcpy(&stdindata, imsg.data, sizeof stdindata);
+			memcpy(&stdindata, data, sizeof stdindata);
 
 			if (c->stdin_callback == NULL)
 				break;
@@ -887,23 +881,12 @@ server_client_msg_dispatch(struct client *c)
 			server_redraw_client(c);
 			recalculate_sizes();
 			break;
-		case MSG_ENVIRON:
-			if (datalen != sizeof environdata)
-				fatalx("bad MSG_ENVIRON size");
-			memcpy(&environdata, imsg.data, sizeof environdata);
-
-			environdata.var[(sizeof environdata.var) - 1] = '\0';
-			if (strchr(environdata.var, '=') != NULL)
-				environ_put(&c->environ, environdata.var);
-			break;
 		case MSG_SHELL:
 			if (datalen != 0)
 				fatalx("bad MSG_SHELL size");
 
 			server_client_msg_shell(c);
 			break;
-		default:
-			fatalx("unexpected message");
 		}
 
 		imsg_free(&imsg);
@@ -912,15 +895,26 @@ server_client_msg_dispatch(struct client *c)
 
 /* Handle command message. */
 void
-server_client_msg_command(struct client *c, struct msg_command_data *data)
+server_client_msg_command(struct client *c, struct imsg *imsg)
 {
-	struct cmd_list	*cmdlist = NULL;
-	int		 argc;
-	char	       **argv, *cause;
+	struct msg_command_data	  data;
+	char			 *buf;
+	size_t			  len;
+	struct cmd_list		 *cmdlist = NULL;
+	int			  argc;
+	char			**argv, *cause;
 
-	argc = data->argc;
-	data->argv[(sizeof data->argv) - 1] = '\0';
-	if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) {
+	if (imsg->hdr.len - IMSG_HEADER_SIZE < sizeof data)
+		fatalx("bad MSG_COMMAND size");
+	memcpy(&data, imsg->data, sizeof data);
+
+	buf = (char*)imsg->data + sizeof data;
+	len = imsg->hdr.len  - IMSG_HEADER_SIZE - sizeof data;
+	if (len > 0 && buf[len - 1] != '\0')
+		fatalx("bad MSG_COMMAND string");
+
+	argc = data.argc;
+	if (cmd_unpack_argv(buf, len, argc, &argv) != 0) {
 		cmdq_error(c->cmdq, "command too long");
 		goto error;
 	}
@@ -951,44 +945,99 @@ error:
 
 /* Handle identify message. */
 void
-server_client_msg_identify(
-    struct client *c, struct msg_identify_data *data, int fd)
+server_client_msg_identify(struct client *c, struct imsg *imsg)
 {
-	c->cwd = NULL;
-	data->cwd[(sizeof data->cwd) - 1] = '\0';
-	if (*data->cwd != '\0')
-		c->cwd = xstrdup(data->cwd);
+	const char	*data;
+	size_t	 	 datalen;
+	int		 flags;
 
-	if (data->flags & IDENTIFY_CONTROL) {
+	if (c->flags & CLIENT_IDENTIFIED)
+		fatalx("out-of-order identify message");
+
+	data = imsg->data;
+	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+
+	switch (imsg->hdr.type)	{
+	case MSG_IDENTIFY_FLAGS:
+		if (datalen != sizeof flags)
+			fatalx("bad MSG_IDENTIFY_FLAGS size");
+		memcpy(&flags, data, sizeof flags);
+		c->flags |= flags;
+		break;
+	case MSG_IDENTIFY_TERM:
+		if (datalen == 0 || data[datalen - 1] != '\0')
+			fatalx("bad MSG_IDENTIFY_TERM string");
+		c->term = xstrdup(data);
+		break;
+	case MSG_IDENTIFY_TTYNAME:
+		if (datalen == 0 || data[datalen - 1] != '\0')
+			fatalx("bad MSG_IDENTIFY_TTYNAME string");
+		c->ttyname = xstrdup(data);
+		break;
+	case MSG_IDENTIFY_CWD:
+		if (datalen != 0)
+			fatalx("bad MSG_IDENTIFY_CWD size");
+		c->cwd = imsg->fd;
+		break;
+	case MSG_IDENTIFY_STDIN:
+		if (datalen != 0)
+			fatalx("bad MSG_IDENTIFY_STDIN size");
+		c->fd = imsg->fd;
+		break;
+	case MSG_IDENTIFY_ENVIRON:
+		if (datalen == 0 || data[datalen - 1] != '\0')
+			fatalx("bad MSG_IDENTIFY_ENVIRON string");
+		if (strchr(data, '=') != NULL)
+			environ_put(&c->environ, data);
+		break;
+	default:
+		break;
+	}
+
+	if (imsg->hdr.type != MSG_IDENTIFY_DONE)
+		return;
+	c->flags |= CLIENT_IDENTIFIED;
+
+#ifdef __CYGWIN__
+	c->fd = open(c->ttyname, O_RDWR|O_NOCTTY);
+	c->cwd = open(".", O_RDONLY);
+#endif
+
+	if (c->flags & CLIENT_CONTROL) {
 		c->stdin_callback = control_callback;
+
 		evbuffer_free(c->stderr_data);
 		c->stderr_data = c->stdout_data;
-		c->flags |= CLIENT_CONTROL;
-		if (data->flags & IDENTIFY_TERMIOS)
+
+		if (c->flags & CLIENT_CONTROLCONTROL)
 			evbuffer_add_printf(c->stdout_data, "\033P1000p");
 		server_write_client(c, MSG_STDIN, NULL, 0);
 
 		c->tty.fd = -1;
 		c->tty.log_fd = -1;
 
-		close(fd);
+		close(c->fd);
+		c->fd = -1;
+
 		return;
 	}
 
-	if (!isatty(fd)) {
-		close(fd);
+	if (c->fd == -1)
+		return;
+	if (!isatty(c->fd)) {
+		close(c->fd);
+		c->fd = -1;
 		return;
 	}
-	data->term[(sizeof data->term) - 1] = '\0';
-	tty_init(&c->tty, c, fd, data->term);
-	if (data->flags & IDENTIFY_UTF8)
+	tty_init(&c->tty, c, c->fd, c->term);
+	if (c->flags & CLIENT_UTF8)
 		c->tty.flags |= TTY_UTF8;
-	if (data->flags & IDENTIFY_256COLOURS)
+	if (c->flags & CLIENT_256COLOURS)
 		c->tty.term_flags |= TERM_256COLOURS;
 
 	tty_resize(&c->tty);
 
-	if (!(data->flags & IDENTIFY_CONTROL))
+	if (!(c->flags & CLIENT_CONTROL))
 		c->flags |= CLIENT_TERMINAL;
 }
 
@@ -996,16 +1045,12 @@ server_client_msg_identify(
 void
 server_client_msg_shell(struct client *c)
 {
-	struct msg_shell_data	 data;
-	const char		*shell;
+	const char	*shell;
 
 	shell = options_get_string(&global_s_options, "default-shell");
-
 	if (*shell == '\0' || areshell(shell))
 		shell = _PATH_BSHELL;
-	if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell)
-		strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell);
+	server_write_client(c, MSG_SHELL, shell, strlen(shell) + 1);
 
-	server_write_client(c, MSG_SHELL, &data, sizeof data);
 	c->flags |= CLIENT_BAD;	/* it will die after exec */
 }
diff --git a/server-fn.c b/server-fn.c
index 86e2054e..4fc4eb5c 100644
--- a/server-fn.c
+++ b/server-fn.c
@@ -56,8 +56,8 @@ server_write_ready(struct client *c)
 }
 
 int
-server_write_client(
-    struct client *c, enum msgtype type, const void *buf, size_t len)
+server_write_client(struct client *c, enum msgtype type, const void *buf,
+    size_t len)
 {
 	struct imsgbuf	*ibuf = &c->ibuf;
 	int              error;
@@ -73,8 +73,8 @@ server_write_client(
 }
 
 void
-server_write_session(
-    struct session *s, enum msgtype type, const void *buf, size_t len)
+server_write_session(struct session *s, enum msgtype type, const void *buf,
+    size_t len)
 {
 	struct client	*c;
 	u_int		 i;
@@ -235,9 +235,7 @@ server_lock_session(struct session *s)
 void
 server_lock_client(struct client *c)
 {
-	const char		*cmd;
-	size_t			 cmdlen;
-	struct msg_lock_data	 lockdata;
+	const char	*cmd;
 
 	if (c->flags & CLIENT_CONTROL)
 		return;
@@ -246,8 +244,7 @@ server_lock_client(struct client *c)
 		return;
 
 	cmd = options_get_string(&c->session->options, "lock-command");
-	cmdlen = strlcpy(lockdata.cmd, cmd, sizeof lockdata.cmd);
-	if (cmdlen >= sizeof lockdata.cmd)
+	if (strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
 		return;
 
 	tty_stop_tty(&c->tty);
@@ -256,7 +253,7 @@ server_lock_client(struct client *c)
 	tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3));
 
 	c->flags |= CLIENT_SUSPENDED;
-	server_write_client(c, MSG_LOCK, &lockdata, sizeof lockdata);
+	server_write_client(c, MSG_LOCK, cmd, strlen(cmd) + 1);
 }
 
 void
@@ -398,14 +395,15 @@ void
 server_destroy_session_group(struct session *s)
 {
 	struct session_group	*sg;
+	struct session		*s1;
 
 	if ((sg = session_group_find(s)) == NULL)
 		server_destroy_session(s);
 	else {
-		TAILQ_FOREACH(s, &sg->sessions, gentry)
+		TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
 			server_destroy_session(s);
-		TAILQ_REMOVE(&session_groups, sg, entry);
-		free(sg);
+			session_destroy(s);
+		}
 	}
 }
 
diff --git a/session.c b/session.c
index 74eb06a5..66a52bc6 100644
--- a/session.c
+++ b/session.c
@@ -84,9 +84,8 @@ session_find_by_id(u_int id)
 
 /* Create a new session. */
 struct session *
-session_create(const char *name, const char *cmd, const char *cwd,
-    struct environ *env, struct termios *tio, int idx, u_int sx, u_int sy,
-    char **cause)
+session_create(const char *name, const char *cmd, int cwd, struct environ *env,
+    struct termios *tio, int idx, u_int sx, u_int sy, char **cause)
 {
 	struct session	*s;
 
@@ -98,7 +97,7 @@ session_create(const char *name, const char *cmd, const char *cwd,
 		fatal("gettimeofday failed");
 	session_update_activity(s);
 
-	s->cwd = xstrdup(cwd);
+	s->cwd = dup(cwd);
 
 	s->curw = NULL;
 	TAILQ_INIT(&s->lastw);
@@ -150,6 +149,7 @@ void
 session_destroy(struct session *s)
 {
 	struct winlink	*wl;
+
 	log_debug("session %s destroyed", s->name);
 
 	RB_REMOVE(sessions, &sessions, s);
@@ -169,7 +169,7 @@ session_destroy(struct session *s)
 		winlink_remove(&s->windows, wl);
 	}
 
-	free(s->cwd);
+	close(s->cwd);
 
 	RB_INSERT(sessions, &dead_sessions, s);
 }
@@ -225,8 +225,8 @@ session_previous_session(struct session *s)
 
 /* Create a new window on a session. */
 struct winlink *
-session_new(struct session *s,
-    const char *name, const char *cmd, const char *cwd, int idx, char **cause)
+session_new(struct session *s, const char *name, const char *cmd, int cwd,
+    int idx, char **cause)
 {
 	struct window	*w;
 	struct winlink	*wl;
@@ -249,8 +249,8 @@ session_new(struct session *s,
 		shell = _PATH_BSHELL;
 
 	hlimit = options_get_number(&s->options, "history-limit");
-	w = window_create(
-	    name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy, hlimit, cause);
+	w = window_create(name, cmd, shell, cwd, &env, s->tio, s->sx, s->sy,
+	    hlimit, cause);
 	if (w == NULL) {
 		winlink_remove(&s->windows, wl);
 		environ_free(&env);
@@ -614,7 +614,7 @@ session_renumber_windows(struct session *s)
 	memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
 	TAILQ_INIT(&s->lastw);
 	TAILQ_FOREACH(wl, &old_lastw, sentry) {
-		wl_new = winlink_find_by_index(&s->windows, wl->idx);
+		wl_new = winlink_find_by_window(&s->windows, wl->window);
 		if (wl_new != NULL)
 			TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry);
 	}
diff --git a/tmux.1 b/tmux.1
index 11251790..56cb3f8f 100644
--- a/tmux.1
+++ b/tmux.1
@@ -99,7 +99,9 @@ Force
 .Nm
 to assume the terminal supports 256 colours.
 .It Fl C
-Start in control mode.
+Start in control mode (see the
+.Sx CONTROL MODE
+section).
 Given twice
 .Xo ( Fl CC ) Xc
 disables echo.
@@ -566,6 +568,7 @@ The following commands are available to manage clients and sessions:
 .Bl -tag -width Ds
 .It Xo Ic attach-session
 .Op Fl dr
+.Op Fl c Ar working-directory
 .Op Fl t Ar target-session
 .Xc
 .D1 (alias: Ic attach )
@@ -599,6 +602,10 @@ needs to select the most recently used session, it will prefer the most
 recently used
 .Em unattached
 session.
+.Pp
+.Fl c
+will set the session working directory (used for new windows) to
+.Ar working-directory .
 .It Xo Ic detach-client
 .Op Fl P
 .Op Fl a
@@ -626,9 +633,10 @@ If it does exist, exit with 0.
 Kill the
 .Nm
 server and clients and destroy all sessions.
-.It Ic kill-session
+.It Xo Ic kill-session
 .Op Fl a
 .Op Fl t Ar target-session
+.Xc
 Destroy the given session, closing any windows linked to it and no other
 sessions, and detaching all clients attached to it.
 If
@@ -673,6 +681,7 @@ Lock all clients attached to
 .Ar target-session .
 .It Xo Ic new-session
 .Op Fl AdDP
+.Op Fl c Ar start-directory
 .Op Fl F Ar format
 .Op Fl n Ar window-name
 .Op Fl s Ar session-name
@@ -1509,13 +1518,6 @@ is not specified, the value of the
 option is used.
 .Fl c
 specifies the working directory in which the new window is created.
-It may have an absolute path or one of the following values (or a subdirectory):
-.Bl -column "XXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXX" -offset indent
-.It Li "Empty string" Ta "Current pane's directory"
-.It Li "~" Ta "User's home directory"
-.It Li "-" Ta "Where session was started"
-.It Li "." Ta "Where server was started"
-.El
 .Pp
 When the shell command completes, the window closes.
 See the
@@ -2175,15 +2177,6 @@ The default is an empty string, which instructs
 to create a login shell using the value of the
 .Ic default-shell
 option.
-.It Ic default-path Ar path
-Set the default working directory for new panes.
-If empty (the default), the working directory is determined from the process
-running in the active pane, from the command line environment or from the
-working directory where the session was created.
-Otherwise the same options are available as for the
-.Fl c
-flag to
-.Ic new-window .
 .It Ic default-shell Ar path
 Specify the default shell.
 This is used as the login shell for new windows when the
@@ -2710,8 +2703,8 @@ The default is on.
 Control automatic window renaming.
 When this setting is enabled,
 .Nm
-will attempt - on supported platforms - to rename the window to reflect the
-command currently running in it.
+will rename the window automatically using the format specified by
+.Ic automatic-rename-format .
 This flag is automatically disabled for an individual window when a name
 is specified at creation with
 .Ic new-window
@@ -2725,6 +2718,13 @@ It may be switched off globally with:
 set-window-option -g automatic-rename off
 .Ed
 .Pp
+.It Ic automatic-rename-format Ar format
+The format (see
+.Sx FORMATS )
+used when the
+.Ic automatic-rename
+option is enabled.
+.Pp
 .It Ic c0-change-interval Ar interval
 .It Ic c0-change-trigger Ar trigger
 These two options configure a simple form of rate limiting for a pane.
@@ -2738,8 +2738,8 @@ instead redraw it entirely every
 .Ar interval
 milliseconds.
 This helps to prevent fast output (such as
-.Xr yes 1
-overwhelming the terminal).
+.Xr yes 1 )
+overwhelming the terminal.
 The default is a trigger of 250 and an interval of 100.
 A trigger of zero disables the rate limiting.
 .Pp
@@ -3026,6 +3026,12 @@ will include the string
 if the session is attached and the string
 .Ql not attached
 if it is unattached.
+A limit may be placed on the length of the resultant string by prefixing it
+by an
+.Ql = ,
+a number and a colon, so
+.Ql #{=10:pane_title}
+will include at most the first 10 characters of the pane title.
 .Pp
 The following variables are available, where appropriate:
 .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX"
@@ -3039,7 +3045,6 @@ The following variables are available, where appropriate:
 .It Li "client_activity_string" Ta "" Ta "String time client last had activity"
 .It Li "client_created" Ta "" Ta "Integer time client created"
 .It Li "client_created_string" Ta "" Ta "String time client created"
-.It Li "client_cwd" Ta "" Ta "Working directory of client"
 .It Li "client_height" Ta "" Ta "Height of client"
 .It Li "client_last_session" Ta "" Ta "Name of the client's last session"
 .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed"
@@ -3067,7 +3072,6 @@ The following variables are available, where appropriate:
 .It Li "mouse_utf8_flag" Ta "" Ta "Pane mouse UTF-8 flag"
 .It Li "pane_active" Ta "" Ta "1 if active pane"
 .It Li "pane_current_command" Ta "" Ta "Current command if available"
-.It Li "pane_current_path" Ta "" Ta "Current path if available"
 .It Li "pane_dead" Ta "" Ta "1 if pane is dead"
 .It Li "pane_height" Ta "" Ta "Height of pane"
 .It Li "pane_id" Ta "#D" Ta "Unique pane ID"
@@ -3096,6 +3100,9 @@ The following variables are available, where appropriate:
 .It Li "session_width" Ta "" Ta "Width of session"
 .It Li "session_windows" Ta "" Ta "Number of windows in session"
 .It Li "window_active" Ta "" Ta "1 if window active"
+.It Li "window_activity_flag" Ta "" Ta "1 if window has activity alert"
+.It Li "window_bell_flag" Ta "" Ta "1 if window has bell"
+.It Li "window_content_flag" Ta "" Ta "1 if window has content alert"
 .It Li "window_find_matches" Ta "" Ta "Matched data from the find-window"
 .It Li "window_flags" Ta "#F" Ta "Window flags"
 .It Li "window_height" Ta "" Ta "Height of window"
@@ -3104,6 +3111,7 @@ The following variables are available, where appropriate:
 .It Li "window_layout" Ta "" Ta "Window layout description"
 .It Li "window_name" Ta "#W" Ta "Name of window"
 .It Li "window_panes" Ta "" Ta "Number of panes in window"
+.It Li "window_silence_flag" Ta "" Ta "1 if window has silence alert"
 .It Li "window_width" Ta "" Ta "Width of window"
 .It Li "wrap_flag" Ta "" Ta "Pane wrap flag"
 .El
@@ -3562,7 +3570,7 @@ If the command doesn't return success, the exit status is also displayed.
 .D1 (alias: Ic info )
 Show server information and terminal details.
 .It Xo Ic wait-for
-.Fl LSU
+.Op Fl L | S | U
 .Ar channel
 .Xc
 .D1 (alias: Ic wait )
@@ -3803,4 +3811,4 @@ bind-key S command-prompt "new-window -n %1 'ssh %1'"
 .Sh SEE ALSO
 .Xr pty 4
 .Sh AUTHORS
-.An Nicholas Marriott Aq nicm@users.sourceforge.net
+.An Nicholas Marriott Aq Mt nicm@users.sourceforge.net
diff --git a/tmux.c b/tmux.c
index 606c574f..1e6edd90 100644
--- a/tmux.c
+++ b/tmux.c
@@ -48,11 +48,8 @@ time_t		 start_time;
 char		 socket_path[MAXPATHLEN];
 int		 login_shell;
 char		*environ_path;
-pid_t		 environ_pid = -1;
-int		 environ_session_id = -1;
 
 __dead void	 usage(void);
-void	 	 parseenvironment(void);
 char 		*makesocketpath(const char *);
 
 #ifndef HAVE___PROGNAME
@@ -127,39 +124,6 @@ areshell(const char *shell)
 	return (0);
 }
 
-const char*
-get_full_path(const char *wd, const char *path)
-{
-	static char	newpath[MAXPATHLEN];
-	char		oldpath[MAXPATHLEN];
-
-	if (getcwd(oldpath, sizeof oldpath) == NULL)
-		return (NULL);
-	if (chdir(wd) != 0)
-		return (NULL);
-	if (realpath(path, newpath) != 0)
-		return (NULL);
-	chdir(oldpath);
-	return (newpath);
-}
-
-void
-parseenvironment(void)
-{
-	char	*env, path[256];
-	long	 pid;
-	int	 id;
-
-	if ((env = getenv("TMUX")) == NULL)
-		return;
-
-	if (sscanf(env, "%255[^,],%ld,%d", path, &pid, &id) != 3)
-		return;
-	environ_path = xstrdup(path);
-	environ_pid = pid;
-	environ_session_id = id;
-}
-
 char *
 makesocketpath(const char *label)
 {
@@ -184,7 +148,8 @@ makesocketpath(const char *label)
 		errno = ENOTDIR;
 		return (NULL);
 	}
-	if (sb.st_uid != uid || (sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) {
+	if (sb.st_uid != uid || (!S_ISDIR(sb.st_mode) &&
+		sb.st_mode & (S_IRWXG|S_IRWXO)) != 0) {
 		errno = EACCES;
 		return (NULL);
 	}
@@ -240,8 +205,10 @@ int
 main(int argc, char **argv)
 {
 	struct passwd	*pw;
-	char		*s, *path, *label, *home, **var;
-	int	 	 opt, flags, quiet, keys;
+	char		*s, *path, *label, *home, **var, tmp[MAXPATHLEN];
+	char		 in[256];
+	long long	 pid;
+	int	 	 opt, flags, quiet, keys, session;
 
 #if defined(DEBUG) && defined(__OpenBSD__)
 	malloc_options = (char *) "AFGJPX";
@@ -255,17 +222,17 @@ main(int argc, char **argv)
 	while ((opt = getopt(argc, argv, "2c:Cdf:lL:qS:uUVv")) != -1) {
 		switch (opt) {
 		case '2':
-			flags |= IDENTIFY_256COLOURS;
+			flags |= CLIENT_256COLOURS;
 			break;
 		case 'c':
 			free(shell_cmd);
 			shell_cmd = xstrdup(optarg);
 			break;
 		case 'C':
-			if (flags & IDENTIFY_CONTROL)
-				flags |= IDENTIFY_TERMIOS;
+			if (flags & CLIENT_CONTROL)
+				flags |= CLIENT_CONTROLCONTROL;
 			else
-				flags |= IDENTIFY_CONTROL;
+				flags |= CLIENT_CONTROL;
 			break;
 		case 'V':
 			printf("%s %s\n", __progname, VERSION);
@@ -289,7 +256,7 @@ main(int argc, char **argv)
 			path = xstrdup(optarg);
 			break;
 		case 'u':
-			flags |= IDENTIFY_UTF8;
+			flags |= CLIENT_UTF8;
 			break;
 		case 'v':
 			debug_level++;
@@ -304,7 +271,7 @@ main(int argc, char **argv)
 	if (shell_cmd != NULL && argc != 0)
 		usage();
 
-	if (!(flags & IDENTIFY_UTF8)) {
+	if (!(flags & CLIENT_UTF8)) {
 		/*
 		 * If the user has set whichever of LC_ALL, LC_CTYPE or LANG
 		 * exist (in that order) to contain UTF-8, it is a safe
@@ -318,12 +285,14 @@ main(int argc, char **argv)
 		}
 		if (s != NULL && (strcasestr(s, "UTF-8") != NULL ||
 		    strcasestr(s, "UTF8") != NULL))
-			flags |= IDENTIFY_UTF8;
+			flags |= CLIENT_UTF8;
 	}
 
 	environ_init(&global_environ);
 	for (var = environ; *var != NULL; var++)
 		environ_put(&global_environ, *var);
+	if (getcwd(tmp, sizeof tmp) != NULL)
+		environ_set(&global_environ, "PWD", tmp);
 
 	options_init(&global_options, NULL);
 	options_table_populate_tree(server_options_table, &global_options);
@@ -337,7 +306,7 @@ main(int argc, char **argv)
 	options_table_populate_tree(window_options_table, &global_w_options);
 
 	/* Enable UTF-8 if the first client is on UTF-8 terminal. */
-	if (flags & IDENTIFY_UTF8) {
+	if (flags & CLIENT_UTF8) {
 		options_set_number(&global_s_options, "status-utf8", 1);
 		options_set_number(&global_s_options, "mouse-utf8", 1);
 		options_set_number(&global_w_options, "utf8", 1);
@@ -370,11 +339,15 @@ main(int argc, char **argv)
 		}
 	}
 
+	/* Get path from environment. */
+	s = getenv("TMUX");
+	if (s != NULL && sscanf(s, "%255[^,],%lld,%d", in, &pid, &session) == 3)
+		environ_path = xstrdup(in);
+
 	/*
 	 * Figure out the socket path. If specified on the command-line with -S
 	 * or -L, use it, otherwise try $TMUX or assume -L default.
 	 */
-	parseenvironment();
 	if (path == NULL) {
 		/* If no -L, use the environment. */
 		if (label == NULL) {
@@ -387,7 +360,8 @@ main(int argc, char **argv)
 		/* -L or default set. */
 		if (label != NULL) {
 			if ((path = makesocketpath(label)) == NULL) {
-				fprintf(stderr, "can't create socket\n");
+				fprintf(stderr, "can't create socket: %s\n",
+					strerror(errno));
 				exit(1);
 			}
 		}
diff --git a/tmux.h b/tmux.h
index cc1c79e8..3a298840 100644
--- a/tmux.h
+++ b/tmux.h
@@ -19,7 +19,7 @@
 #ifndef TMUX_H
 #define TMUX_H
 
-#define PROTOCOL_VERSION 7
+#define PROTOCOL_VERSION 8
 
 #include <sys/param.h>
 #include <sys/time.h>
@@ -51,14 +51,6 @@ extern char   **environ;
 /* Automatic name refresh interval, in milliseconds. */
 #define NAME_INTERVAL 500
 
-/*
- * Maximum sizes of strings in message data. Don't forget to bump
- * PROTOCOL_VERSION if any of these change!
- */
-#define COMMAND_LENGTH 2048	/* packed argv size */
-#define TERMINAL_LENGTH 128	/* length of TERM environment variable */
-#define ENVIRON_LENGTH 1024	/* environment variable length */
-
 /*
  * UTF-8 data size. This must be big enough to hold combined characters as well
  * as single.
@@ -144,7 +136,7 @@ extern char   **environ;
 	"[layout #{window_layout}] #{window_id}"		\
 	"#{?window_active, (active),}";
 #define LIST_WINDOWS_WITH_SESSION_TEMPLATE			\
-	"#{session_name}: "					\
+	"#{session_name}:"					\
 	"#{window_index}: #{window_name}#{window_flags} "	\
 	"(#{window_panes} panes) "				\
 	"[#{window_width}x#{window_height}] "
@@ -430,27 +422,33 @@ ARRAY_DECL(causelist, char *);
 
 /* Message codes. */
 enum msgtype {
-	MSG_COMMAND,
+	MSG_VERSION = 12,
+
+	MSG_IDENTIFY_FLAGS = 100,
+	MSG_IDENTIFY_TERM,
+	MSG_IDENTIFY_TTYNAME,
+	MSG_IDENTIFY_CWD,
+	MSG_IDENTIFY_STDIN,
+	MSG_IDENTIFY_ENVIRON,
+	MSG_IDENTIFY_DONE,
+
+	MSG_COMMAND = 200,
 	MSG_DETACH,
-	MSG_ERROR,
+	MSG_DETACHKILL,
 	MSG_EXIT,
 	MSG_EXITED,
 	MSG_EXITING,
-	MSG_IDENTIFY,
-	MSG_STDIN,
+	MSG_LOCK,
 	MSG_READY,
 	MSG_RESIZE,
-	MSG_SHUTDOWN,
-	MSG_SUSPEND,
-	MSG_VERSION,
-	MSG_WAKEUP,
-	MSG_ENVIRON,
-	MSG_UNLOCK,
-	MSG_LOCK,
 	MSG_SHELL,
+	MSG_SHUTDOWN,
 	MSG_STDERR,
+	MSG_STDIN,
 	MSG_STDOUT,
-	MSG_DETACHKILL
+	MSG_SUSPEND,
+	MSG_UNLOCK,
+	MSG_WAKEUP,
 };
 
 /*
@@ -459,45 +457,8 @@ enum msgtype {
  * Don't forget to bump PROTOCOL_VERSION if any of these change!
  */
 struct msg_command_data {
-	pid_t		pid;		/* from $TMUX or -1 */
-	int		session_id;	/* from $TMUX or -1 */
-
-	int		argc;
-	char		argv[COMMAND_LENGTH];
-};
-
-struct msg_identify_data {
-	char		cwd[MAXPATHLEN];
-
-	char		term[TERMINAL_LENGTH];
-
-#ifdef __CYGWIN__
-	char		ttyname[TTY_NAME_MAX];
-#endif
-
-#define IDENTIFY_UTF8 0x1
-#define IDENTIFY_256COLOURS 0x2
-/* 0x4 unused */
-#define IDENTIFY_CONTROL 0x8
-#define IDENTIFY_TERMIOS 0x10
-	int		flags;
-};
-
-struct msg_lock_data {
-	char		cmd[COMMAND_LENGTH];
-};
-
-struct msg_environ_data {
-	char		var[ENVIRON_LENGTH];
-};
-
-struct msg_shell_data {
-	char		shell[MAXPATHLEN];
-};
-
-struct msg_exit_data {
-	int		retcode;
-};
+	int	argc;
+}; /* followed by packed argv */
 
 struct msg_stdin_data {
 	ssize_t	size;
@@ -945,7 +906,7 @@ struct window_pane {
 
 	char		*cmd;
 	char		*shell;
-	char		*cwd;
+	int		 cwd;
 
 	pid_t		 pid;
 	char		 tty[TTY_NAME_MAX];
@@ -1005,6 +966,7 @@ struct window {
 #define WINDOW_REDRAW 0x4
 #define WINDOW_SILENCE 0x8
 #define WINDOW_ZOOMED 0x10
+#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE)
 
 	struct options	 options;
 
@@ -1091,7 +1053,7 @@ struct session {
 	u_int		 id;
 
 	char		*name;
-	char		*cwd;
+	int		 cwd;
 
 	struct timeval	 creation_time;
 	struct timeval	 activity_time;
@@ -1290,8 +1252,10 @@ RB_HEAD(status_out_tree, status_out);
 /* Client connection. */
 struct client {
 	struct imsgbuf	 ibuf;
+
+	int		 fd;
 	struct event	 event;
-	int		 retcode;
+	int		 retval;
 
 	struct timeval	 creation_time;
 	struct timeval	 activity_time;
@@ -1299,8 +1263,10 @@ struct client {
 	struct environ	 environ;
 
 	char		*title;
-	char		*cwd;
+	int		 cwd;
 
+	char		*term;
+	char		*ttyname;
 	struct tty	 tty;
 
 	void		(*stdin_callback)(struct client *, int, void *);
@@ -1322,7 +1288,7 @@ struct client {
 #define CLIENT_EXIT 0x4
 #define CLIENT_REDRAW 0x8
 #define CLIENT_STATUS 0x10
-#define CLIENT_REPEAT 0x20 /* allow command to repeat within repeat time */
+#define CLIENT_REPEAT 0x20
 #define CLIENT_SUSPENDED 0x40
 #define CLIENT_BAD 0x80
 #define CLIENT_IDENTIFY 0x100
@@ -1331,7 +1297,11 @@ struct client {
 #define CLIENT_READONLY 0x800
 #define CLIENT_REDRAWWINDOW 0x1000
 #define CLIENT_CONTROL 0x2000
-#define CLIENT_FOCUSED 0x4000
+#define CLIENT_CONTROLCONTROL 0x4000
+#define CLIENT_FOCUSED 0x8000
+#define CLIENT_UTF8 0x10000
+#define CLIENT_256COLOURS 0x20000
+#define CLIENT_IDENTIFIED 0x40000
 	int		 flags;
 
 	struct event	 identify_timer;
@@ -1428,8 +1398,6 @@ struct cmd_q {
 	void			 (*emptyfn)(struct cmd_q *);
 	void			*data;
 
-	struct msg_command_data	*msgdata;
-
 	TAILQ_ENTRY(cmd_q)       waitentry;
 };
 
@@ -1446,12 +1414,10 @@ struct cmd_entry {
 
 #define CMD_STARTSERVER 0x1
 #define CMD_CANTNEST 0x2
-#define CMD_SENDENVIRON 0x4
-#define CMD_READONLY 0x8
+#define CMD_READONLY 0x4
 	int		 flags;
 
 	void		 (*key_binding)(struct cmd *, int);
-	int		 (*check)(struct args *);
 	enum cmd_retval	 (*exec)(struct cmd *, struct cmd_q *);
 };
 
@@ -1525,13 +1491,10 @@ extern time_t	 start_time;
 extern char	 socket_path[MAXPATHLEN];
 extern int	 login_shell;
 extern char	*environ_path;
-extern pid_t	 environ_pid;
-extern int	 environ_session_id;
 void		 logfile(const char *);
 const char	*getshell(void);
 int		 checkshell(const char *);
 int		 areshell(const char *);
-const char*	 get_full_path(const char *, const char *);
 void		 setblocking(int, int);
 __dead void	 shell_exec(const char *, const char *);
 
@@ -1549,16 +1512,19 @@ int		 format_cmp(struct format_entry *, struct format_entry *);
 RB_PROTOTYPE(format_tree, format_entry, entry, format_cmp);
 struct format_tree *format_create(void);
 void		 format_free(struct format_tree *);
-void printflike3 format_add(
-		     struct format_tree *, const char *, const char *, ...);
+void printflike3 format_add(struct format_tree *, const char *, const char *,
+		     ...);
 const char	*format_find(struct format_tree *, const char *);
 char		*format_expand(struct format_tree *, const char *);
 void		 format_session(struct format_tree *, struct session *);
 void		 format_client(struct format_tree *, struct client *);
-void		 format_winlink(
-		     struct format_tree *, struct session *, struct winlink *);
-void		 format_window_pane(struct format_tree *, struct window_pane *);
-void		 format_paste_buffer(struct format_tree *, struct paste_buffer *);
+void		 format_window(struct format_tree *, struct window *);
+void		 format_winlink(struct format_tree *, struct session *,
+		     struct winlink *);
+void		 format_window_pane(struct format_tree *,
+		     struct window_pane *);
+void		 format_paste_buffer(struct format_tree *,
+		     struct paste_buffer *);
 
 /* mode-key.c */
 extern const struct mode_key_table mode_key_tables[];
@@ -1764,7 +1730,6 @@ int		 cmd_find_index(struct cmd_q *, const char *,
 struct winlink	*cmd_find_pane(struct cmd_q *, const char *, struct session **,
 		     struct window_pane **);
 char		*cmd_template_replace(const char *, const char *, int);
-const char     	*cmd_get_default_path(struct cmd_q *, const char *);
 extern const struct cmd_entry *cmd_table[];
 extern const struct cmd_entry cmd_attach_session_entry;
 extern const struct cmd_entry cmd_bind_key_entry;
@@ -1855,7 +1820,8 @@ extern const struct cmd_entry cmd_up_pane_entry;
 extern const struct cmd_entry cmd_wait_for_entry;
 
 /* cmd-attach-session.c */
-enum cmd_retval	 cmd_attach_session(struct cmd_q *, const char*, int, int);
+enum cmd_retval	 cmd_attach_session(struct cmd_q *, const char *, int, int,
+		     const char *);
 
 /* cmd-list.c */
 struct cmd_list	*cmd_list_parse(int, char **, const char *, u_int, char **);
@@ -1868,7 +1834,7 @@ int		 cmdq_free(struct cmd_q *);
 void printflike2 cmdq_print(struct cmd_q *, const char *, ...);
 void printflike2 cmdq_info(struct cmd_q *, const char *, ...);
 void printflike2 cmdq_error(struct cmd_q *, const char *, ...);
-int		 cmdq_guard(struct cmd_q *, const char *);
+int		 cmdq_guard(struct cmd_q *, const char *, int);
 void		 cmdq_run(struct cmd_q *, struct cmd_list *);
 void		 cmdq_append(struct cmd_q *, struct cmd_list *);
 int		 cmdq_continue(struct cmd_q *);
@@ -1919,10 +1885,10 @@ void	 server_window_loop(void);
 /* server-fn.c */
 void	 server_fill_environ(struct session *, struct environ *);
 void	 server_write_ready(struct client *);
-int	 server_write_client(
-	     struct client *, enum msgtype, const void *, size_t);
-void	 server_write_session(
-	     struct session *, enum msgtype, const void *, size_t);
+int	 server_write_client(struct client *, enum msgtype, const void *,
+	     size_t);
+void	 server_write_session(struct session *, enum msgtype, const void *,
+	     size_t);
 void	 server_redraw_client(struct client *);
 void	 server_status_client(struct client *);
 void	 server_redraw_session(struct session *);
@@ -2144,9 +2110,9 @@ void		 winlink_stack_remove(struct winlink_stack *, struct winlink *);
 int		 window_index(struct window *, u_int *);
 struct window	*window_find_by_id(u_int);
 struct window	*window_create1(u_int, u_int);
-struct window	*window_create(const char *, const char *, const char *,
-		     const char *, struct environ *, struct termios *,
-		     u_int, u_int, u_int, char **);
+struct window	*window_create(const char *, const char *, const char *, int,
+		     struct environ *, struct termios *, u_int, u_int, u_int,
+		     char **);
 void		 window_destroy(struct window *);
 struct window_pane *window_get_active_at(struct window *, u_int, u_int);
 void		 window_set_active_at(struct window *, u_int, u_int);
@@ -2170,8 +2136,8 @@ struct window_pane *window_pane_create(struct window *, u_int, u_int, u_int);
 void		 window_pane_destroy(struct window_pane *);
 void		 window_pane_timer_start(struct window_pane *);
 int		 window_pane_spawn(struct window_pane *, const char *,
-		     const char *, const char *, struct environ *,
-		     struct termios *, char **);
+		     const char *, int, struct environ *, struct termios *,
+		     char **);
 void		 window_pane_resize(struct window_pane *, u_int, u_int);
 void		 window_pane_alternate_on(struct window_pane *,
 		     struct grid_cell *, int);
@@ -2272,8 +2238,10 @@ void	window_choose_collapse_all(struct window_pane *);
 void	window_choose_set_current(struct window_pane *, u_int);
 
 /* names.c */
-void		 queue_window_name(struct window *);
-char		*default_window_name(struct window *);
+void	 queue_window_name(struct window *);
+char	*default_window_name(struct window *);
+char	*format_window_name(struct window *);
+char	*parse_window_name(const char *);
 
 /* signal.c */
 void	set_signals(void(*)(int, short, void *));
@@ -2305,7 +2273,7 @@ RB_PROTOTYPE(sessions, session, entry, session_cmp);
 int		 session_alive(struct session *);
 struct session	*session_find(const char *);
 struct session	*session_find_by_id(u_int);
-struct session	*session_create(const char *, const char *, const char *,
+struct session	*session_create(const char *, const char *, int,
 		     struct environ *, struct termios *, int, u_int, u_int,
 		     char **);
 void		 session_destroy(struct session *);
@@ -2313,8 +2281,8 @@ int		 session_check_name(const char *);
 void		 session_update_activity(struct session *);
 struct session	*session_next_session(struct session *);
 struct session	*session_previous_session(struct session *);
-struct winlink	*session_new(struct session *,
-		     const char *, const char *, const char *, int, char **);
+struct winlink	*session_new(struct session *, const char *, const char *, int,
+		     int, char **);
 struct winlink	*session_attach(
 		     struct session *, struct window *, int, char **);
 int		 session_detach(struct session *, struct winlink *);
@@ -2340,10 +2308,8 @@ int	utf8_append(struct utf8_data *, u_char);
 u_int	utf8_combine(const struct utf8_data *);
 u_int	utf8_split2(u_int, u_char *);
 
-/* osdep-*.c */
-char		*osdep_get_name(int, char *);
-char		*osdep_get_cwd(int);
-struct event_base *osdep_event_init(void);
+/* procname.c */
+char   *get_proc_name(int, char *);
 
 /* log.c */
 void		 log_open(int, const char *);
diff --git a/tty-keys.c b/tty-keys.c
index 3055f399..595ad6e1 100644
--- a/tty-keys.c
+++ b/tty-keys.c
@@ -676,11 +676,17 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
 		log_debug("mouse input: %.*s", (int) *size, buf);
 
 		/* Check and return the mouse input. */
-		if (b < 32 || x < 33 || y < 33)
+		if (b < 32)
 			return (-1);
 		b -= 32;
-		x -= 33;
-		y -= 33;
+		if (x >= 33)
+			x -= 33;
+		else
+			x = 256 - x;
+		if (y >= 33)
+			y -= 33;
+		else
+			y = 256 - y;
 	} else if (buf[2] == '<') {
 		/* Read the three inputs. */
 		*size = 3;
@@ -740,6 +746,8 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
 	m->sgr = sgr;
 	m->sgr_xb = sgr_b;
 	m->sgr_rel = sgr_rel;
+	m->x = x;
+	m->y = y;
 	if (b & 64) { /* wheel button */
 		b &= 3;
 		if (b == 0)
@@ -767,8 +775,6 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size)
 		}
 		m->button = (b & 3);
 	}
-	m->x = x;
-	m->y = y;
 
 	return (0);
 }
diff --git a/window-choose.c b/window-choose.c
index 5ed85f0e..572581a5 100644
--- a/window-choose.c
+++ b/window-choose.c
@@ -81,6 +81,7 @@ int     window_choose_key_index(struct window_choose_mode_data *, u_int);
 int     window_choose_index_key(struct window_choose_mode_data *, int);
 void	window_choose_prompt_input(enum window_choose_input_type,
 	    const char *, struct window_pane *, int);
+void	window_choose_reset_top(struct window_pane *, u_int);
 
 void
 window_choose_add(struct window_pane *wp, struct window_choose_data *wcd)
@@ -107,8 +108,17 @@ window_choose_set_current(struct window_pane *wp, u_int cur)
 	struct screen			*s = &data->screen;
 
 	data->selected = cur;
-	if (data->selected > screen_size_y(s) - 1)
-		data->top = ARRAY_LENGTH(&data->list) - screen_size_y(s);
+	window_choose_reset_top(wp, screen_size_y(s));
+}
+
+void
+window_choose_reset_top(struct window_pane *wp, u_int sy)
+{
+	struct window_choose_mode_data	*data = wp->modedata;
+
+	data->top = 0;
+	if (data->selected > sy - 1)
+		data->top = data->selected - (sy - 1);
 
 	window_choose_redraw_screen(wp);
 }
@@ -277,10 +287,7 @@ window_choose_resize(struct window_pane *wp, u_int sx, u_int sy)
 	struct window_choose_mode_data	*data = wp->modedata;
 	struct screen			*s = &data->screen;
 
-	data->top = 0;
-	if (data->selected > sy - 1)
-		data->top = data->selected - (sy - 1);
-
+	window_choose_reset_top(wp, sy);
 	screen_resize(s, sx, sy, 0);
 	window_choose_redraw_screen(wp);
 }
@@ -373,6 +380,7 @@ window_choose_collapse_all(struct window_pane *wp)
 {
 	struct window_choose_mode_data	*data = wp->modedata;
 	struct window_choose_mode_item	*item;
+	struct screen			*scr = &data->screen;
 	struct session			*s, *chosen;
 	u_int				 i;
 
@@ -391,7 +399,7 @@ window_choose_collapse_all(struct window_pane *wp)
 		if (item->wcd->type & TREE_SESSION)
 			data->selected = i;
 	}
-	window_choose_redraw_screen(wp);
+	window_choose_reset_top(wp, screen_size_y(scr));
 }
 
 void
@@ -399,6 +407,7 @@ window_choose_expand_all(struct window_pane *wp)
 {
 	struct window_choose_mode_data	*data = wp->modedata;
 	struct window_choose_mode_item	*item;
+	struct screen			*scr = &data->screen;
 	struct session			*s;
 	u_int				 i;
 
@@ -414,7 +423,7 @@ window_choose_expand_all(struct window_pane *wp)
 		}
 	}
 
-	window_choose_redraw_screen(wp);
+	window_choose_reset_top(wp, screen_size_y(scr));
 }
 
 void
diff --git a/window.c b/window.c
index 7678adc6..9f47f444 100644
--- a/window.c
+++ b/window.c
@@ -306,7 +306,7 @@ window_create1(u_int sx, u_int sy)
 
 struct window *
 window_create(const char *name, const char *cmd, const char *shell,
-    const char *cwd, struct environ *env, struct termios *tio,
+    int cwd, struct environ *env, struct termios *tio,
     u_int sx, u_int sy, u_int hlimit, char **cause)
 {
 	struct window		*w;
@@ -672,7 +672,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
 
 	wp->cmd = NULL;
 	wp->shell = NULL;
-	wp->cwd = NULL;
+	wp->cwd = -1;
 
 	wp->fd = -1;
 	wp->event = NULL;
@@ -727,7 +727,7 @@ window_pane_destroy(struct window_pane *wp)
 
 	RB_REMOVE(window_pane_tree, &all_window_panes, wp);
 
-	free(wp->cwd);
+	close(wp->cwd);
 	free(wp->shell);
 	free(wp->cmd);
 	free(wp);
@@ -735,7 +735,7 @@ window_pane_destroy(struct window_pane *wp)
 
 int
 window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
-    const char *cwd, struct environ *env, struct termios *tio, char **cause)
+    int cwd, struct environ *env, struct termios *tio, char **cause)
 {
 	struct winsize	 ws;
 	char		*argv0, paneid[16];
@@ -754,9 +754,9 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
 		free(wp->shell);
 		wp->shell = xstrdup(shell);
 	}
-	if (cwd != NULL) {
-		free(wp->cwd);
-		wp->cwd = xstrdup(cwd);
+	if (cwd != -1) {
+		close(wp->cwd);
+		wp->cwd = dup(cwd);
 	}
 
 	log_debug("spawn: %s -- %s", wp->shell, wp->cmd);
@@ -771,7 +771,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell,
 		xasprintf(cause, "%s: %s", cmd, strerror(errno));
 		return (-1);
 	case 0:
-		if (chdir(wp->cwd) != 0)
+		if (fchdir(wp->cwd) != 0)
 			chdir("/");
 
 		if (tcgetattr(STDIN_FILENO, &tio2) != 0)
@@ -1243,6 +1243,7 @@ winlink_clear_flags(struct winlink *wl)
 				continue;
 
 			wm->flags &= ~WINLINK_ALERTFLAGS;
+			wm->window->flags &= ~WINDOW_ALERTFLAGS;
 			server_status_session(s);
 		}
 	}