mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Merge branch 'master' into 3.2-rc
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/travis/build-all.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/travis/build-all.sh
									
									
									
									
										vendored
									
									
								
							| @@ -35,4 +35,4 @@ tar -zxf ncurses-*.tar.gz || exit 1 | ||||
|  | ||||
| sh autogen.sh || exit 1 | ||||
| PKG_CONFIG_PATH=$BUILD/lib/pkgconfig ./configure --prefix=$BUILD "$@" | ||||
| make && make install || exit 1 | ||||
| make && make install || (cat config.log; exit 1) | ||||
|   | ||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -19,3 +19,5 @@ configure | ||||
| tmux.1.* | ||||
| *.dSYM | ||||
| cmd-parse.c | ||||
| fuzz/*-fuzzer | ||||
| .dirstamp | ||||
|   | ||||
							
								
								
									
										70
									
								
								CHANGES
									
									
									
									
									
								
							
							
						
						
									
										70
									
								
								CHANGES
									
									
									
									
									
								
							| @@ -1,5 +1,69 @@ | ||||
| CHANGES FROM 3.1c TO 3.2 | ||||
|  | ||||
| * Improve performance of format evaluation. | ||||
|  | ||||
| * Make jump command support UTF-8 in copy mode. | ||||
|  | ||||
| * Support X11 colour names and other colour formats for OSC 10 and 11. | ||||
|  | ||||
| * Add "pipe" variants of "copy-pipe" commands which do not copy. | ||||
|  | ||||
| * Include "focused" in client flags. | ||||
|  | ||||
| * Send Unicode directional isolate characters around horizontal pane borders if | ||||
|   the terminal supports UTF-8 and an extension terminfo(5) capability "Bidi" is | ||||
|   present. | ||||
|  | ||||
| * Add a -S flag to new-window to make it select the existing window if one | ||||
|   with the given name already exists rather than failing with an error. | ||||
|  | ||||
| * Addd a format modifier to check if a window or session name exists (N/w or | ||||
|   N/s). | ||||
|  | ||||
| * Add compat clock_gettime for older macOS. | ||||
|  | ||||
| * Add a no-detached choice to detach-on-destroy which detaches only if there | ||||
|   are no other detached sessions to switch to. | ||||
|  | ||||
| * Add rectangle-on and rectangle-off copy mode commands. | ||||
|  | ||||
| * Change so that window_flags escapes # automatically. A new format | ||||
|   window_raw_flags contains the old unescaped version. | ||||
|  | ||||
| * Add -N flag to never start server even if command would normally do so. | ||||
|  | ||||
| * With incremental search, start empty and only repeat the previous search if | ||||
|   the user tries to search again with an empty prompt. | ||||
|  | ||||
| * Add a value for remain-on-exit that only keeps the pane if the program | ||||
|   failed. | ||||
|  | ||||
| * Add a -C flag to run-shell to use a tmux command rather than a shell command. | ||||
|  | ||||
| * Do not list user options with show-hooks. | ||||
|  | ||||
| * Remove current match indicator in copy mode which can't work anymore since we | ||||
|   only search the visible region. | ||||
|  | ||||
| * Make synchronize-panes a pane option and add -U flag to set-option to unset | ||||
|   an option on all panes. | ||||
|  | ||||
| * Make replacement of ##s consistent when drawing formats, whether followed by | ||||
|   [ or not. Add a flag (e) to the q: format modifier to double up #s | ||||
|  | ||||
| * Add -N flag to display-panes to ignore keys. | ||||
|  | ||||
| * Change how escaping is processed for formats so that ## and # can be used in | ||||
|   styles. | ||||
|  | ||||
| * Add a 'w' format modifier for string width. | ||||
|  | ||||
| * Add support for Haiku. | ||||
|  | ||||
| * Expand menu and popup -x and -y as formats. | ||||
|  | ||||
| * Add numeric comparisons for formats. | ||||
|  | ||||
| * Fire focus events even when the pane is in a mode. | ||||
|  | ||||
| * Add -O flag to display-menu to not automatically close when all mouse buttons | ||||
| @@ -267,9 +331,9 @@ CHANGES FROM 3.1c TO 3.2 | ||||
| * Change default position for display-menu -x and -y to centre rather than top | ||||
|   left. | ||||
|  | ||||
| * Add support for per-client transient popups, similar to menus. These are | ||||
|   created with new command display-popup. Popups may either show fixed text and | ||||
|   trigger a tmux command when a key is pressed, or run a program (-R flag). | ||||
| * Add support for per-client transient popups, similar to menus but which are | ||||
|   connected to an external command (like a pane). These are created with new | ||||
|   command display-popup. | ||||
|  | ||||
| * Change double and triple click bindings so that only one is fired (previously | ||||
|   double click was fired on the way to triple click). Also add default double | ||||
|   | ||||
							
								
								
									
										14
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								Makefile.am
									
									
									
									
									
								
							| @@ -28,7 +28,7 @@ AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations | ||||
| AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare | ||||
| AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align | ||||
| AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes | ||||
| AM_CFLAGS += -Wno-unused-result | ||||
| AM_CFLAGS += -Wno-unused-result -Wno-format-y2k | ||||
| AM_CPPFLAGS += -DDEBUG | ||||
| endif | ||||
| AM_CPPFLAGS += -iquote. | ||||
| @@ -58,6 +58,11 @@ if IS_NETBSD | ||||
| AM_CPPFLAGS += -D_OPENBSD_SOURCE | ||||
| endif | ||||
|  | ||||
| # Set flags for Haiku. | ||||
| if IS_HAIKU | ||||
| AM_CPPFLAGS += -D_BSD_SOURCE | ||||
| endif | ||||
|  | ||||
| # List of sources. | ||||
| dist_tmux_SOURCES = \ | ||||
| 	alerts.c \ | ||||
| @@ -136,6 +141,7 @@ dist_tmux_SOURCES = \ | ||||
| 	file.c \ | ||||
| 	format.c \ | ||||
| 	format-draw.c \ | ||||
| 	grid-reader.c \ | ||||
| 	grid-view.c \ | ||||
| 	grid.c \ | ||||
| 	input-keys.c \ | ||||
| @@ -197,6 +203,12 @@ if HAVE_UTF8PROC | ||||
| nodist_tmux_SOURCES += compat/utf8proc.c | ||||
| endif | ||||
|  | ||||
| if NEED_FUZZING | ||||
| check_PROGRAMS = fuzz/input-fuzzer | ||||
| fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS) | ||||
| fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS) | ||||
| endif | ||||
|  | ||||
| # Install tmux.1 in the right format. | ||||
| install-exec-hook: | ||||
| 	if test x@MANFORMAT@ = xmdoc; then \ | ||||
|   | ||||
							
								
								
									
										1
									
								
								alerts.c
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								alerts.c
									
									
									
									
									
								
							| @@ -18,7 +18,6 @@ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <event.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|   | ||||
							
								
								
									
										30
									
								
								cfg.c
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								cfg.c
									
									
									
									
									
								
							| @@ -27,12 +27,15 @@ | ||||
| #include "tmux.h" | ||||
|  | ||||
| struct client		 *cfg_client; | ||||
| static char		 *cfg_file; | ||||
| int			  cfg_finished; | ||||
| static char		**cfg_causes; | ||||
| static u_int		  cfg_ncauses; | ||||
| static struct cmdq_item	 *cfg_item; | ||||
|  | ||||
| int                       cfg_quiet = 1; | ||||
| char                    **cfg_files; | ||||
| u_int                     cfg_nfiles; | ||||
|  | ||||
| static enum cmd_retval | ||||
| cfg_client_done(__unused struct cmdq_item *item, __unused void *data) | ||||
| { | ||||
| @@ -59,19 +62,11 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data) | ||||
| 	return (CMD_RETURN_NORMAL); | ||||
| } | ||||
|  | ||||
| void | ||||
| set_cfg_file(const char *path) | ||||
| { | ||||
| 	free(cfg_file); | ||||
| 	cfg_file = xstrdup(path); | ||||
| } | ||||
|  | ||||
| void | ||||
| start_cfg(void) | ||||
| { | ||||
| 	struct client	 *c; | ||||
| 	char		**paths; | ||||
| 	u_int		  i, n; | ||||
| 	u_int		  i; | ||||
|  | ||||
| 	/* | ||||
| 	 * Configuration files are loaded without a client, so commands are run | ||||
| @@ -89,15 +84,12 @@ start_cfg(void) | ||||
| 		cmdq_append(c, cfg_item); | ||||
| 	} | ||||
|  | ||||
| 	if (cfg_file == NULL) { | ||||
| 		expand_paths(TMUX_CONF, &paths, &n); | ||||
| 		for (i = 0; i < n; i++) { | ||||
| 			load_cfg(paths[i], c, NULL, CMD_PARSE_QUIET, NULL); | ||||
| 			free(paths[i]); | ||||
| 		} | ||||
| 		free(paths); | ||||
| 	} else | ||||
| 		load_cfg(cfg_file, c, NULL, 0, NULL); | ||||
| 	for (i = 0; i < cfg_nfiles; i++) { | ||||
| 		if (cfg_quiet) | ||||
| 			load_cfg(cfg_files[i], c, NULL, CMD_PARSE_QUIET, NULL); | ||||
| 		else | ||||
| 			load_cfg(cfg_files[i], c, NULL, 0, NULL); | ||||
| 	} | ||||
|  | ||||
| 	cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); | ||||
| } | ||||
|   | ||||
							
								
								
									
										343
									
								
								client.c
									
									
									
									
									
								
							
							
						
						
									
										343
									
								
								client.c
									
									
									
									
									
								
							| @@ -24,7 +24,6 @@ | ||||
| #include <sys/file.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <event.h> | ||||
| #include <fcntl.h> | ||||
| #include <signal.h> | ||||
| #include <stdlib.h> | ||||
| @@ -62,7 +61,8 @@ static __dead void	 client_exec(const char *,const char *); | ||||
| static int		 client_get_lock(char *); | ||||
| static int		 client_connect(struct event_base *, const char *, | ||||
| 			     uint64_t); | ||||
| static void		 client_send_identify(const char *, const char *, int); | ||||
| static void		 client_send_identify(const char *, const char *, | ||||
| 			     char **, u_int, const char *, int); | ||||
| static void		 client_signal(int); | ||||
| static void		 client_dispatch(struct imsg *, void *); | ||||
| static void		 client_dispatch_attached(struct imsg *); | ||||
| @@ -127,6 +127,8 @@ retry: | ||||
| 		log_debug("connect failed: %s", strerror(errno)); | ||||
| 		if (errno != ECONNREFUSED && errno != ENOENT) | ||||
| 			goto failed; | ||||
| 		if (flags & CLIENT_NOSTARTSERVER) | ||||
| 			goto failed; | ||||
| 		if (~flags & CLIENT_STARTSERVER) | ||||
| 			goto failed; | ||||
| 		close(fd); | ||||
| @@ -221,20 +223,7 @@ client_exit_message(void) | ||||
| static void | ||||
| client_exit(void) | ||||
| { | ||||
| 	struct client_file	*cf; | ||||
| 	size_t			 left; | ||||
| 	int			 waiting = 0; | ||||
|  | ||||
| 	RB_FOREACH (cf, client_files, &client_files) { | ||||
| 		if (cf->event == NULL) | ||||
| 			continue; | ||||
| 		left = EVBUFFER_LENGTH(cf->event->output); | ||||
| 		if (left != 0) { | ||||
| 			waiting++; | ||||
| 			log_debug("file %u %zu bytes left", cf->stream, left); | ||||
| 		} | ||||
| 	} | ||||
| 	if (waiting == 0) | ||||
| 	if (!file_write_left(&client_files)) | ||||
| 		proc_exit(client_proc); | ||||
| } | ||||
|  | ||||
| @@ -246,13 +235,14 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, | ||||
| 	struct cmd_parse_result	*pr; | ||||
| 	struct msg_command	*data; | ||||
| 	int			 fd, i; | ||||
| 	const char		*ttynam, *cwd; | ||||
| 	const char		*ttynam, *termname, *cwd; | ||||
| 	pid_t			 ppid; | ||||
| 	enum msgtype		 msg; | ||||
| 	struct termios		 tio, saved_tio; | ||||
| 	size_t			 size, linesize = 0; | ||||
| 	ssize_t			 linelen; | ||||
| 	char			*line = NULL; | ||||
| 	char			*line = NULL, **caps = NULL, *cause; | ||||
| 	u_int			 ncaps = 0; | ||||
|  | ||||
| 	/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ | ||||
| 	signal(SIGCHLD, SIG_IGN); | ||||
| @@ -308,6 +298,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, | ||||
| 		cwd = "/"; | ||||
| 	if ((ttynam = ttyname(STDIN_FILENO)) == NULL) | ||||
| 		ttynam = ""; | ||||
| 	if ((termname = getenv("TERM")) == NULL) | ||||
| 		termname = ""; | ||||
|  | ||||
| 	/* | ||||
| 	 * Drop privileges for client. "proc exec" is needed for -c and for | ||||
| @@ -323,6 +315,16 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, | ||||
| 	    NULL) != 0) | ||||
| 		fatal("pledge failed"); | ||||
|  | ||||
| 	/* Load terminfo entry if any. */ | ||||
| 	if (isatty(STDIN_FILENO) && | ||||
| 	    *termname != '\0' && | ||||
| 	    tty_term_read_list(termname, STDIN_FILENO, &caps, &ncaps, | ||||
| 	    &cause) != 0) { | ||||
| 		fprintf(stderr, "%s\n", cause); | ||||
| 		free(cause); | ||||
| 		return (1); | ||||
| 	} | ||||
|  | ||||
| 	/* Free stuff that is not used in the client. */ | ||||
| 	if (ptm_fd != -1) | ||||
| 		close(ptm_fd); | ||||
| @@ -353,7 +355,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, | ||||
| 	} | ||||
|  | ||||
| 	/* Send identify messages. */ | ||||
| 	client_send_identify(ttynam, cwd, feat); | ||||
| 	client_send_identify(ttynam, termname, caps, ncaps, cwd, feat); | ||||
| 	tty_term_free_list(caps, ncaps); | ||||
|  | ||||
| 	/* Send first command. */ | ||||
| 	if (msg == MSG_COMMAND) { | ||||
| @@ -436,27 +439,32 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, | ||||
|  | ||||
| /* Send identify messages to server. */ | ||||
| static void | ||||
| client_send_identify(const char *ttynam, const char *cwd, int feat) | ||||
| client_send_identify(const char *ttynam, const char *termname, char **caps, | ||||
|     u_int ncaps, const char *cwd, int feat) | ||||
| { | ||||
| 	const char	 *s; | ||||
| 	char		**ss; | ||||
| 	size_t		  sslen; | ||||
| 	int		  fd, flags = client_flags; | ||||
| 	pid_t		  pid; | ||||
| 	char	**ss; | ||||
| 	size_t	  sslen; | ||||
| 	int	  fd, flags = client_flags; | ||||
| 	pid_t	  pid; | ||||
| 	u_int	  i; | ||||
|  | ||||
| 	proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); | ||||
| 	proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags, | ||||
| 	    sizeof client_flags); | ||||
|  | ||||
| 	if ((s = getenv("TERM")) == NULL) | ||||
| 		s = ""; | ||||
| 	proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); | ||||
| 	proc_send(client_peer, MSG_IDENTIFY_TERM, -1, termname, | ||||
| 	    strlen(termname) + 1); | ||||
| 	proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat); | ||||
|  | ||||
| 	proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, | ||||
| 	    strlen(ttynam) + 1); | ||||
| 	proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); | ||||
|  | ||||
| 	for (i = 0; i < ncaps; i++) { | ||||
| 		proc_send(client_peer, MSG_IDENTIFY_TERMINFO, -1, | ||||
| 		    caps[i], strlen(caps[i]) + 1); | ||||
| 	} | ||||
|  | ||||
| 	if ((fd = dup(STDIN_FILENO)) == -1) | ||||
| 		fatal("dup failed"); | ||||
| 	proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); | ||||
| @@ -477,257 +485,6 @@ client_send_identify(const char *ttynam, const char *cwd, int feat) | ||||
| 	proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); | ||||
| } | ||||
|  | ||||
| /* File write error callback. */ | ||||
| static void | ||||
| client_write_error_callback(__unused struct bufferevent *bev, | ||||
|     __unused short what, void *arg) | ||||
| { | ||||
| 	struct client_file	*cf = arg; | ||||
|  | ||||
| 	log_debug("write error file %d", cf->stream); | ||||
|  | ||||
| 	bufferevent_free(cf->event); | ||||
| 	cf->event = NULL; | ||||
|  | ||||
| 	close(cf->fd); | ||||
| 	cf->fd = -1; | ||||
|  | ||||
| 	if (client_exitflag) | ||||
| 		client_exit(); | ||||
| } | ||||
|  | ||||
| /* File write callback. */ | ||||
| static void | ||||
| client_write_callback(__unused struct bufferevent *bev, void *arg) | ||||
| { | ||||
| 	struct client_file	*cf = arg; | ||||
|  | ||||
| 	if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { | ||||
| 		bufferevent_free(cf->event); | ||||
| 		close(cf->fd); | ||||
| 		RB_REMOVE(client_files, &client_files, cf); | ||||
| 		file_free(cf); | ||||
| 	} | ||||
|  | ||||
| 	if (client_exitflag) | ||||
| 		client_exit(); | ||||
| } | ||||
|  | ||||
| /* Open write file. */ | ||||
| static void | ||||
| client_write_open(void *data, size_t datalen) | ||||
| { | ||||
| 	struct msg_write_open	*msg = data; | ||||
| 	const char		*path; | ||||
| 	struct msg_write_ready	 reply; | ||||
| 	struct client_file	 find, *cf; | ||||
| 	const int		 flags = O_NONBLOCK|O_WRONLY|O_CREAT; | ||||
| 	int			 error = 0; | ||||
|  | ||||
| 	if (datalen < sizeof *msg) | ||||
| 		fatalx("bad MSG_WRITE_OPEN size"); | ||||
| 	if (datalen == sizeof *msg) | ||||
| 		path = "-"; | ||||
| 	else | ||||
| 		path = (const char *)(msg + 1); | ||||
| 	log_debug("open write file %d %s", msg->stream, path); | ||||
|  | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { | ||||
| 		cf = file_create(NULL, msg->stream, NULL, NULL); | ||||
| 		RB_INSERT(client_files, &client_files, cf); | ||||
| 	} else { | ||||
| 		error = EBADF; | ||||
| 		goto reply; | ||||
| 	} | ||||
| 	if (cf->closed) { | ||||
| 		error = EBADF; | ||||
| 		goto reply; | ||||
| 	} | ||||
|  | ||||
| 	cf->fd = -1; | ||||
| 	if (msg->fd == -1) | ||||
| 		cf->fd = open(path, msg->flags|flags, 0644); | ||||
| 	else { | ||||
| 		if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO) | ||||
| 			errno = EBADF; | ||||
| 		else { | ||||
| 			cf->fd = dup(msg->fd); | ||||
| 			if (~client_flags & CLIENT_CONTROL) | ||||
| 				close(msg->fd); /* can only be used once */ | ||||
| 		} | ||||
| 	} | ||||
| 	if (cf->fd == -1) { | ||||
| 		error = errno; | ||||
| 		goto reply; | ||||
| 	} | ||||
|  | ||||
| 	cf->event = bufferevent_new(cf->fd, NULL, client_write_callback, | ||||
| 	    client_write_error_callback, cf); | ||||
| 	bufferevent_enable(cf->event, EV_WRITE); | ||||
| 	goto reply; | ||||
|  | ||||
| reply: | ||||
| 	reply.stream = msg->stream; | ||||
| 	reply.error = error; | ||||
| 	proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply); | ||||
| } | ||||
|  | ||||
| /* Write to client file. */ | ||||
| static void | ||||
| client_write_data(void *data, size_t datalen) | ||||
| { | ||||
| 	struct msg_write_data	*msg = data; | ||||
| 	struct client_file	 find, *cf; | ||||
| 	size_t			 size = datalen - sizeof *msg; | ||||
|  | ||||
| 	if (datalen < sizeof *msg) | ||||
| 		fatalx("bad MSG_WRITE size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) | ||||
| 		fatalx("unknown stream number"); | ||||
| 	log_debug("write %zu to file %d", size, cf->stream); | ||||
|  | ||||
| 	if (cf->event != NULL) | ||||
| 		bufferevent_write(cf->event, msg + 1, size); | ||||
| } | ||||
|  | ||||
| /* Close client file. */ | ||||
| static void | ||||
| client_write_close(void *data, size_t datalen) | ||||
| { | ||||
| 	struct msg_write_close	*msg = data; | ||||
| 	struct client_file	 find, *cf; | ||||
|  | ||||
| 	if (datalen != sizeof *msg) | ||||
| 		fatalx("bad MSG_WRITE_CLOSE size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) | ||||
| 		fatalx("unknown stream number"); | ||||
| 	log_debug("close file %d", cf->stream); | ||||
|  | ||||
| 	if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { | ||||
| 		if (cf->event != NULL) | ||||
| 			bufferevent_free(cf->event); | ||||
| 		if (cf->fd != -1) | ||||
| 			close(cf->fd); | ||||
| 		RB_REMOVE(client_files, &client_files, cf); | ||||
| 		file_free(cf); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* File read callback. */ | ||||
| static void | ||||
| client_read_callback(__unused struct bufferevent *bev, void *arg) | ||||
| { | ||||
| 	struct client_file	*cf = arg; | ||||
| 	void			*bdata; | ||||
| 	size_t			 bsize; | ||||
| 	struct msg_read_data	*msg; | ||||
| 	size_t			 msglen; | ||||
|  | ||||
| 	msg = xmalloc(sizeof *msg); | ||||
| 	for (;;) { | ||||
| 		bdata = EVBUFFER_DATA(cf->event->input); | ||||
| 		bsize = EVBUFFER_LENGTH(cf->event->input); | ||||
|  | ||||
| 		if (bsize == 0) | ||||
| 			break; | ||||
| 		if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) | ||||
| 			bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; | ||||
| 		log_debug("read %zu from file %d", bsize, cf->stream); | ||||
|  | ||||
| 		msglen = (sizeof *msg) + bsize; | ||||
| 		msg = xrealloc(msg, msglen); | ||||
| 		msg->stream = cf->stream; | ||||
| 		memcpy(msg + 1, bdata, bsize); | ||||
| 		proc_send(client_peer, MSG_READ, -1, msg, msglen); | ||||
|  | ||||
| 		evbuffer_drain(cf->event->input, bsize); | ||||
| 	} | ||||
| 	free(msg); | ||||
| } | ||||
|  | ||||
| /* File read error callback. */ | ||||
| static void | ||||
| client_read_error_callback(__unused struct bufferevent *bev, | ||||
|     __unused short what, void *arg) | ||||
| { | ||||
| 	struct client_file	*cf = arg; | ||||
| 	struct msg_read_done	 msg; | ||||
|  | ||||
| 	log_debug("read error file %d", cf->stream); | ||||
|  | ||||
| 	msg.stream = cf->stream; | ||||
| 	msg.error = 0; | ||||
| 	proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg); | ||||
|  | ||||
| 	bufferevent_free(cf->event); | ||||
| 	close(cf->fd); | ||||
| 	RB_REMOVE(client_files, &client_files, cf); | ||||
| 	file_free(cf); | ||||
| } | ||||
|  | ||||
| /* Open read file. */ | ||||
| static void | ||||
| client_read_open(void *data, size_t datalen) | ||||
| { | ||||
| 	struct msg_read_open	*msg = data; | ||||
| 	const char		*path; | ||||
| 	struct msg_read_done	 reply; | ||||
| 	struct client_file	 find, *cf; | ||||
| 	const int		 flags = O_NONBLOCK|O_RDONLY; | ||||
| 	int			 error; | ||||
|  | ||||
| 	if (datalen < sizeof *msg) | ||||
| 		fatalx("bad MSG_READ_OPEN size"); | ||||
| 	if (datalen == sizeof *msg) | ||||
| 		path = "-"; | ||||
| 	else | ||||
| 		path = (const char *)(msg + 1); | ||||
| 	log_debug("open read file %d %s", msg->stream, path); | ||||
|  | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { | ||||
| 		cf = file_create(NULL, msg->stream, NULL, NULL); | ||||
| 		RB_INSERT(client_files, &client_files, cf); | ||||
| 	} else { | ||||
| 		error = EBADF; | ||||
| 		goto reply; | ||||
| 	} | ||||
| 	if (cf->closed) { | ||||
| 		error = EBADF; | ||||
| 		goto reply; | ||||
| 	} | ||||
|  | ||||
| 	cf->fd = -1; | ||||
| 	if (msg->fd == -1) | ||||
| 		cf->fd = open(path, flags); | ||||
| 	else { | ||||
| 		if (msg->fd != STDIN_FILENO) | ||||
| 			errno = EBADF; | ||||
| 		else { | ||||
| 			cf->fd = dup(msg->fd); | ||||
| 			if (~client_flags & CLIENT_CONTROL) | ||||
| 				close(msg->fd); /* can only be used once */ | ||||
| 		} | ||||
| 	} | ||||
| 	if (cf->fd == -1) { | ||||
| 		error = errno; | ||||
| 		goto reply; | ||||
| 	} | ||||
|  | ||||
| 	cf->event = bufferevent_new(cf->fd, client_read_callback, NULL, | ||||
| 	    client_read_error_callback, cf); | ||||
| 	bufferevent_enable(cf->event, EV_READ); | ||||
| 	return; | ||||
|  | ||||
| reply: | ||||
| 	reply.stream = msg->stream; | ||||
| 	reply.error = error; | ||||
| 	proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply); | ||||
| } | ||||
|  | ||||
| /* Run command in shell; used for -c. */ | ||||
| static __dead void | ||||
| client_exec(const char *shell, const char *shellcmd) | ||||
| @@ -802,13 +559,25 @@ client_signal(int sig) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Callback for file write error or close. */ | ||||
| static void | ||||
| client_file_check_cb(__unused struct client *c, __unused const char *path, | ||||
|     __unused int error, __unused int closed, __unused struct evbuffer *buffer, | ||||
|     __unused void *data) | ||||
| { | ||||
| 	if (client_exitflag) | ||||
| 		client_exit(); | ||||
| } | ||||
|  | ||||
| /* Callback for client read events. */ | ||||
| static void | ||||
| client_dispatch(struct imsg *imsg, __unused void *arg) | ||||
| { | ||||
| 	if (imsg == NULL) { | ||||
| 		client_exitreason = CLIENT_EXIT_LOST_SERVER; | ||||
| 		client_exitval = 1; | ||||
| 		if (!client_exitflag) { | ||||
| 			client_exitreason = CLIENT_EXIT_LOST_SERVER; | ||||
| 			client_exitval = 1; | ||||
| 		} | ||||
| 		proc_exit(client_proc); | ||||
| 		return; | ||||
| 	} | ||||
| @@ -916,16 +685,20 @@ client_dispatch_wait(struct imsg *imsg) | ||||
| 		proc_exit(client_proc); | ||||
| 		break; | ||||
| 	case MSG_READ_OPEN: | ||||
| 		client_read_open(data, datalen); | ||||
| 		file_read_open(&client_files, client_peer, imsg, 1, | ||||
| 		    !(client_flags & CLIENT_CONTROL), client_file_check_cb, | ||||
| 		    NULL); | ||||
| 		break; | ||||
| 	case MSG_WRITE_OPEN: | ||||
| 		client_write_open(data, datalen); | ||||
| 		file_write_open(&client_files, client_peer, imsg, 1, | ||||
| 		    !(client_flags & CLIENT_CONTROL), client_file_check_cb, | ||||
| 		    NULL); | ||||
| 		break; | ||||
| 	case MSG_WRITE: | ||||
| 		client_write_data(data, datalen); | ||||
| 		file_write_data(&client_files, imsg); | ||||
| 		break; | ||||
| 	case MSG_WRITE_CLOSE: | ||||
| 		client_write_close(data, datalen); | ||||
| 		file_write_close(&client_files, imsg); | ||||
| 		break; | ||||
| 	case MSG_OLDSTDERR: | ||||
| 	case MSG_OLDSTDIN: | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <paths.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|  | ||||
| @@ -50,10 +51,10 @@ const struct cmd_entry cmd_display_popup_entry = { | ||||
| 	.name = "display-popup", | ||||
| 	.alias = "popup", | ||||
|  | ||||
| 	.args = { "CEKc:d:h:R:t:w:x:y:", 0, -1 }, | ||||
| 	.usage = "[-CEK] [-c target-client] [-d start-directory] [-h height] " | ||||
| 	         "[-R shell-command] " CMD_TARGET_PANE_USAGE " [-w width] " | ||||
| 	         "[-x position] [-y position] [command line ...]", | ||||
| 	.args = { "Cc:d:Eh:t:w:x:y:", 0, -1 }, | ||||
| 	.usage = "[-CE] [-c target-client] [-d start-directory] [-h height] " | ||||
| 	         CMD_TARGET_PANE_USAGE " [-w width] " | ||||
| 	         "[-x position] [-y position] [command]", | ||||
|  | ||||
| 	.target = { 't', CMD_FIND_PANE, 0 }, | ||||
|  | ||||
| @@ -61,7 +62,7 @@ const struct cmd_entry cmd_display_popup_entry = { | ||||
| 	.exec = cmd_display_popup_exec | ||||
| }; | ||||
|  | ||||
| static void | ||||
| static int | ||||
| cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, | ||||
|     struct args *args, u_int *px, u_int *py, u_int w, u_int h) | ||||
| { | ||||
| @@ -71,44 +72,46 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, | ||||
| 	struct session		*s = tc->session; | ||||
| 	struct winlink		*wl = target->wl; | ||||
| 	struct window_pane	*wp = target->wp; | ||||
| 	struct style_ranges	*ranges; | ||||
| 	struct style_range	*sr; | ||||
| 	struct style_ranges	*ranges = NULL; | ||||
| 	struct style_range	*sr = NULL; | ||||
| 	const char		*xp, *yp; | ||||
| 	u_int			 line, ox, oy, sx, sy, lines; | ||||
| 	char			*p; | ||||
| 	int			 top; | ||||
| 	u_int			 line, ox, oy, sx, sy, lines, position; | ||||
| 	long			 n; | ||||
| 	struct format_tree	*ft; | ||||
|  | ||||
| 	lines = status_line_size(tc); | ||||
| 	for (line = 0; line < lines; line++) { | ||||
| 		ranges = &tc->status.entries[line].ranges; | ||||
| 		TAILQ_FOREACH(sr, ranges, entry) { | ||||
| 			if (sr->type == STYLE_RANGE_WINDOW) | ||||
| 				break; | ||||
| 		} | ||||
| 		if (sr != NULL) | ||||
| 			break; | ||||
| 	/* | ||||
| 	 * Work out the position from the -x and -y arguments. This is the | ||||
| 	 * bottom-left position. | ||||
| 	 */ | ||||
|  | ||||
| 	/* If the popup is too big, stop now. */ | ||||
| 	if (w > tty->sx || h > tty->sy) | ||||
| 		return (0); | ||||
|  | ||||
| 	/* Create format with mouse position if any. */ | ||||
| 	ft = format_create_from_target(item); | ||||
| 	if (event->m.valid) { | ||||
| 		format_add(ft, "popup_mouse_x", "%u", event->m.x); | ||||
| 		format_add(ft, "popup_mouse_y", "%u", event->m.y); | ||||
| 	} | ||||
| 	if (line == lines) | ||||
| 		ranges = &tc->status.entries[0].ranges; | ||||
|  | ||||
| 	xp = args_get(args, 'x'); | ||||
| 	if (xp == NULL || strcmp(xp, "C") == 0) | ||||
| 		*px = (tty->sx - 1) / 2 - w / 2; | ||||
| 	else if (strcmp(xp, "R") == 0) | ||||
| 		*px = tty->sx - 1; | ||||
| 	else if (strcmp(xp, "P") == 0) { | ||||
| 		tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); | ||||
| 		if (wp->xoff >= ox) | ||||
| 			*px = wp->xoff - ox; | ||||
| 	/* | ||||
| 	 * If there are any status lines, add this window position and the | ||||
| 	 * status line position. | ||||
| 	 */ | ||||
| 	top = status_at_line(tc); | ||||
| 	if (top != -1) { | ||||
| 		lines = status_line_size(tc); | ||||
| 		if (top == 0) | ||||
| 			top = lines; | ||||
| 		else | ||||
| 			*px = 0; | ||||
| 	} else if (strcmp(xp, "M") == 0) { | ||||
| 		if (event->m.valid && event->m.x > w / 2) | ||||
| 			*px = event->m.x - w / 2; | ||||
| 		else | ||||
| 			*px = 0; | ||||
| 	} else if (strcmp(xp, "W") == 0) { | ||||
| 		if (status_at_line(tc) == -1) | ||||
| 			*px = 0; | ||||
| 		else { | ||||
| 			top = 0; | ||||
| 		position = options_get_number(s->options, "status-position"); | ||||
|  | ||||
| 		for (line = 0; line < lines; line++) { | ||||
| 			ranges = &tc->status.entries[line].ranges; | ||||
| 			TAILQ_FOREACH(sr, ranges, entry) { | ||||
| 				if (sr->type != STYLE_RANGE_WINDOW) | ||||
| 					continue; | ||||
| @@ -116,61 +119,137 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item, | ||||
| 					break; | ||||
| 			} | ||||
| 			if (sr != NULL) | ||||
| 				*px = sr->start; | ||||
| 			else | ||||
| 				*px = 0; | ||||
| 				break; | ||||
| 		} | ||||
| 		if (line == lines) | ||||
| 			ranges = &tc->status.entries[0].ranges; | ||||
|  | ||||
| 		if (sr != NULL) { | ||||
| 			format_add(ft, "popup_window_status_line_x", "%u", | ||||
| 			    sr->start); | ||||
| 			if (position == 0) { | ||||
| 				format_add(ft, "popup_window_status_line_y", | ||||
| 				    "%u", line + 1 + h); | ||||
| 			} else { | ||||
| 				format_add(ft, "popup_window_status_line_y", | ||||
| 				    "%u", tty->sy - lines + line); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (position == 0) | ||||
| 			format_add(ft, "popup_status_line_y", "%u", lines + h); | ||||
| 		else { | ||||
| 			format_add(ft, "popup_status_line_y", "%u", | ||||
| 			    tty->sy - lines); | ||||
| 		} | ||||
| 	} else | ||||
| 		*px = strtoul(xp, NULL, 10); | ||||
| 	if ((*px) + w >= tty->sx) | ||||
| 		*px = tty->sx - w; | ||||
| 		top = 0; | ||||
|  | ||||
| 	/* Popup width and height. */ | ||||
| 	format_add(ft, "popup_width", "%u", w); | ||||
| 	format_add(ft, "popup_height", "%u", h); | ||||
|  | ||||
| 	/* Position so popup is in the centre. */ | ||||
| 	n = (long)(tty->sx - 1) / 2 - w / 2; | ||||
| 	if (n < 0) | ||||
| 		format_add(ft, "popup_centre_x", "%u", 0); | ||||
| 	else | ||||
| 		format_add(ft, "popup_centre_x", "%ld", n); | ||||
| 	n = (tty->sy - 1) / 2 + h / 2; | ||||
| 	if (n >= tty->sy) | ||||
| 		format_add(ft, "popup_centre_y", "%u", tty->sy - h); | ||||
| 	else | ||||
| 		format_add(ft, "popup_centre_y", "%ld", n); | ||||
|  | ||||
| 	/* Position of popup relative to mouse. */ | ||||
| 	if (event->m.valid) { | ||||
| 		n = (long)event->m.x - w / 2; | ||||
| 		if (n < 0) | ||||
| 			format_add(ft, "popup_mouse_centre_x", "%u", 0); | ||||
| 		else | ||||
| 			format_add(ft, "popup_mouse_centre_x", "%ld", n); | ||||
| 		n = event->m.y - h / 2; | ||||
| 		if (n + h >= tty->sy) { | ||||
| 			format_add(ft, "popup_mouse_centre_y", "%u", | ||||
| 			    tty->sy - h); | ||||
| 		} else | ||||
| 			format_add(ft, "popup_mouse_centre_y", "%ld", n); | ||||
| 		n = (long)event->m.y + h; | ||||
| 		if (n + h >= tty->sy) | ||||
| 			format_add(ft, "popup_mouse_top", "%u", tty->sy - h); | ||||
| 		else | ||||
| 			format_add(ft, "popup_mouse_top", "%ld", n); | ||||
| 		n = event->m.y - h; | ||||
| 		if (n < 0) | ||||
| 			format_add(ft, "popup_mouse_bottom", "%u", 0); | ||||
| 		else | ||||
| 			format_add(ft, "popup_mouse_bottom", "%ld", n); | ||||
| 	} | ||||
|  | ||||
| 	/* Position in pane. */ | ||||
| 	tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); | ||||
| 	n = top + wp->yoff - oy + h; | ||||
| 	if (n >= tty->sy) | ||||
| 		format_add(ft, "popup_pane_top", "%u", tty->sy - h); | ||||
| 	else | ||||
| 		format_add(ft, "popup_pane_top", "%ld", n); | ||||
| 	format_add(ft, "popup_pane_bottom", "%u", top + wp->yoff + wp->sy - oy); | ||||
| 	format_add(ft, "popup_pane_left", "%u", wp->xoff - ox); | ||||
| 	n = (long)wp->xoff + wp->sx - ox - w; | ||||
| 	if (n < 0) | ||||
| 		format_add(ft, "popup_pane_right", "%u", 0); | ||||
| 	else | ||||
| 		format_add(ft, "popup_pane_right", "%ld", n); | ||||
|  | ||||
| 	/* Expand horizontal position. */ | ||||
| 	xp = args_get(args, 'x'); | ||||
| 	if (xp == NULL || strcmp(xp, "C") == 0) | ||||
| 		xp = "#{popup_centre_x}"; | ||||
| 	else if (strcmp(xp, "R") == 0) | ||||
| 		xp = "#{popup_right}"; | ||||
| 	else if (strcmp(xp, "P") == 0) | ||||
| 		xp = "#{popup_pane_left}"; | ||||
| 	else if (strcmp(xp, "M") == 0) | ||||
| 		xp = "#{popup_mouse_centre_x}"; | ||||
| 	else if (strcmp(xp, "W") == 0) | ||||
| 		xp = "#{popup_window_status_line_x}"; | ||||
| 	p = format_expand(ft, xp); | ||||
| 	n = strtol(p, NULL, 10); | ||||
| 	if (n + w >= tty->sx) | ||||
| 		n = tty->sx - w; | ||||
| 	else if (n < 0) | ||||
| 		n = 0; | ||||
| 	*px = n; | ||||
| 	log_debug("%s: -x: %s = %s = %u", __func__, xp, p, *px); | ||||
| 	free(p); | ||||
|  | ||||
| 	/* Expand vertical position  */ | ||||
| 	yp = args_get(args, 'y'); | ||||
| 	if (yp == NULL || strcmp(yp, "C") == 0) | ||||
| 		*py = (tty->sy - 1) / 2 + h / 2; | ||||
| 	else if (strcmp(yp, "P") == 0) { | ||||
| 		tty_window_offset(&tc->tty, &ox, &oy, &sx, &sy); | ||||
| 		if (wp->yoff + wp->sy >= oy) | ||||
| 			*py = wp->yoff + wp->sy - oy; | ||||
| 		else | ||||
| 			*py = 0; | ||||
| 	} else if (strcmp(yp, "M") == 0) { | ||||
| 		if (event->m.valid) | ||||
| 			*py = event->m.y + h; | ||||
| 		else | ||||
| 			*py = 0; | ||||
| 	} else if (strcmp(yp, "S") == 0) { | ||||
| 		if (options_get_number(s->options, "status-position") == 0) { | ||||
| 			if (lines != 0) | ||||
| 				*py = lines + h; | ||||
| 			else | ||||
| 				*py = 0; | ||||
| 		} else { | ||||
| 			if (lines != 0) | ||||
| 				*py = tty->sy - lines; | ||||
| 			else | ||||
| 				*py = tty->sy; | ||||
| 		} | ||||
| 	} else if (strcmp(yp, "W") == 0) { | ||||
| 		if (options_get_number(s->options, "status-position") == 0) { | ||||
| 			if (lines != 0) | ||||
| 				*py = line + 1 + h; | ||||
| 			else | ||||
| 				*py = 0; | ||||
| 		} else { | ||||
| 			if (lines != 0) | ||||
| 				*py = tty->sy - lines + line; | ||||
| 			else | ||||
| 				*py = tty->sy; | ||||
| 		} | ||||
| 	} else | ||||
| 		*py = strtoul(yp, NULL, 10); | ||||
| 	if (*py < h) | ||||
| 		*py = 0; | ||||
| 		yp = "#{popup_centre_y}"; | ||||
| 	else if (strcmp(yp, "P") == 0) | ||||
| 		yp = "#{popup_pane_bottom}"; | ||||
| 	else if (strcmp(yp, "M") == 0) | ||||
| 		yp = "#{popup_mouse_top}"; | ||||
| 	else if (strcmp(yp, "S") == 0) | ||||
| 		yp = "#{popup_status_line_y}"; | ||||
| 	else if (strcmp(yp, "W") == 0) | ||||
| 		yp = "#{popup_window_status_line_y}"; | ||||
| 	p = format_expand(ft, yp); | ||||
| 	n = strtol(p, NULL, 10); | ||||
| 	if (n < h) | ||||
| 		n = 0; | ||||
| 	else | ||||
| 		*py -= h; | ||||
| 	if ((*py) + h >= tty->sy) | ||||
| 		*py = tty->sy - h; | ||||
| 		n -= h; | ||||
| 	if (n + h >= tty->sy) | ||||
| 		n = tty->sy - h; | ||||
| 	else if (n < 0) | ||||
| 		n = 0; | ||||
| 	*py = n; | ||||
| 	log_debug("%s: -y: %s = %s = %u", __func__, yp, p, *py); | ||||
| 	free(p); | ||||
|  | ||||
| 	return (1); | ||||
| } | ||||
|  | ||||
| static enum cmd_retval | ||||
| @@ -226,8 +305,11 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 		menu_free(menu); | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
| 	} | ||||
| 	cmd_display_menu_get_position(tc, item, args, &px, &py, menu->width + 4, | ||||
| 	    menu->count + 2); | ||||
| 	if (!cmd_display_menu_get_position(tc, item, args, &px, &py, | ||||
| 	    menu->width + 4, menu->count + 2)) { | ||||
| 		menu_free(menu); | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
| 	} | ||||
|  | ||||
| 	if (args_has(args, 'O')) | ||||
| 		flags |= MENU_STAYOPEN; | ||||
| @@ -244,13 +326,14 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) | ||||
| { | ||||
| 	struct args		*args = cmd_get_args(self); | ||||
| 	struct cmd_find_state	*target = cmdq_get_target(item); | ||||
| 	struct session		*s = target->s; | ||||
| 	struct client		*tc = cmdq_get_target_client(item); | ||||
| 	struct tty		*tty = &tc->tty; | ||||
| 	const char		*value, *cmd = NULL, **lines = NULL; | ||||
| 	const char		*value, *shell[] = { NULL, NULL }; | ||||
| 	const char		*shellcmd = NULL; | ||||
| 	char			*cwd, *cause; | ||||
| 	int			 flags = 0; | ||||
| 	u_int			 px, py, w, h, nlines = 0; | ||||
| 	char			*cwd, *cause, **argv = args->argv; | ||||
| 	int			 flags = 0, argc = args->argc; | ||||
| 	u_int			 px, py, w, h; | ||||
|  | ||||
| 	if (args_has(args, 'C')) { | ||||
| 		server_client_clear_overlay(tc); | ||||
| @@ -259,17 +342,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 	if (tc->overlay_draw != NULL) | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
|  | ||||
| 	if (args->argc >= 1) | ||||
| 		cmd = args->argv[0]; | ||||
| 	if (args->argc >= 2) { | ||||
| 		lines = (const char **)args->argv + 1; | ||||
| 		nlines = args->argc - 1; | ||||
| 	} | ||||
|  | ||||
| 	if (nlines != 0) | ||||
| 		h = popup_height(nlines, lines) + 2; | ||||
| 	else | ||||
| 		h = tty->sy / 2; | ||||
| 	h = tty->sy / 2; | ||||
| 	if (args_has(args, 'h')) { | ||||
| 		h = args_percentage(args, 'h', 1, tty->sy, tty->sy, &cause); | ||||
| 		if (cause != NULL) { | ||||
| @@ -279,10 +352,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (nlines != 0) | ||||
| 		w = popup_width(item, nlines, lines, tc, target) + 2; | ||||
| 	else | ||||
| 		w = tty->sx / 2; | ||||
| 	w = tty->sx / 2; | ||||
| 	if (args_has(args, 'w')) { | ||||
| 		w = args_percentage(args, 'w', 1, tty->sx, tty->sx, &cause); | ||||
| 		if (cause != NULL) { | ||||
| @@ -296,26 +366,33 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 		w = tty->sx - 1; | ||||
| 	if (h > tty->sy - 1) | ||||
| 		h = tty->sy - 1; | ||||
| 	cmd_display_menu_get_position(tc, item, args, &px, &py, w, h); | ||||
| 	if (!cmd_display_menu_get_position(tc, item, args, &px, &py, w, h)) | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
|  | ||||
| 	value = args_get(args, 'd'); | ||||
| 	if (value != NULL) | ||||
| 		cwd = format_single_from_target(item, value); | ||||
| 	else | ||||
| 		cwd = xstrdup(server_client_get_cwd(tc, target->s)); | ||||
| 		cwd = xstrdup(server_client_get_cwd(tc, s)); | ||||
| 	if (argc == 0) | ||||
| 		shellcmd = options_get_string(s->options, "default-command"); | ||||
| 	else if (argc == 1) | ||||
| 		shellcmd = argv[0]; | ||||
| 	if (argc <= 1 && (shellcmd == NULL || *shellcmd == '\0')) { | ||||
| 		shellcmd = NULL; | ||||
| 		shell[0] = options_get_string(s->options, "default-shell"); | ||||
| 		if (!checkshell(shell[0])) | ||||
| 			shell[0] = _PATH_BSHELL; | ||||
| 		argc = 1; | ||||
| 		argv = (char**)shell; | ||||
| 	} | ||||
|  | ||||
| 	value = args_get(args, 'R'); | ||||
| 	if (value != NULL) | ||||
| 		shellcmd = format_single_from_target(item, value); | ||||
|  | ||||
| 	if (args_has(args, 'K')) | ||||
| 		flags |= POPUP_WRITEKEYS; | ||||
| 	if (args_has(args, 'E') > 1) | ||||
| 		flags |= POPUP_CLOSEEXITZERO; | ||||
| 	else if (args_has(args, 'E')) | ||||
| 		flags |= POPUP_CLOSEEXIT; | ||||
| 	if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, | ||||
| 	    cmd, cwd, tc, target, NULL, NULL) != 0) | ||||
| 	if (popup_display(flags, item, px, py, w, h, shellcmd, argc, argv, cwd, | ||||
| 	    tc, s, NULL, NULL) != 0) | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
| 	return (CMD_RETURN_WAIT); | ||||
| } | ||||
|   | ||||
| @@ -34,8 +34,8 @@ const struct cmd_entry cmd_display_panes_entry = { | ||||
| 	.name = "display-panes", | ||||
| 	.alias = "displayp", | ||||
|  | ||||
| 	.args = { "bd:t:", 0, 1 }, | ||||
| 	.usage = "[-b] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", | ||||
| 	.args = { "bd:Nt:", 0, 1 }, | ||||
| 	.usage = "[-bN] [-d duration] " CMD_TARGET_CLIENT_USAGE " [template]", | ||||
|  | ||||
| 	.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG, | ||||
| 	.exec = cmd_display_panes_exec | ||||
| @@ -284,8 +284,15 @@ cmd_display_panes_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 	else | ||||
| 		cdata->item = item; | ||||
|  | ||||
| 	server_client_set_overlay(tc, delay, NULL, NULL, cmd_display_panes_draw, | ||||
| 	    cmd_display_panes_key, cmd_display_panes_free, cdata); | ||||
| 	if (args_has(args, 'N')) { | ||||
| 		server_client_set_overlay(tc, delay, NULL, NULL, | ||||
| 		    cmd_display_panes_draw, NULL, cmd_display_panes_free, | ||||
| 		    cdata); | ||||
| 	} else { | ||||
| 		server_client_set_overlay(tc, delay, NULL, NULL, | ||||
| 		    cmd_display_panes_draw, cmd_display_panes_key, | ||||
| 		    cmd_display_panes_free, cdata); | ||||
| 	} | ||||
|  | ||||
| 	if (args_has(args, 'b')) | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
|   | ||||
| @@ -128,7 +128,7 @@ cmd_if_shell_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 		cdata->input.c->references++; | ||||
| 	cmd_find_copy_state(&cdata->input.fs, target); | ||||
|  | ||||
| 	if (job_run(shellcmd, s, | ||||
| 	if (job_run(shellcmd, 0, NULL, s, | ||||
| 	    server_client_get_cwd(cmdq_get_client(item), s), NULL, | ||||
| 	    cmd_if_shell_callback, cmd_if_shell_free, cdata, 0, -1, | ||||
| 	    -1) == NULL) { | ||||
|   | ||||
| @@ -142,7 +142,10 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 	src_wp->window = dst_w; | ||||
| 	options_set_parent(src_wp->options, dst_w->options); | ||||
| 	src_wp->flags |= PANE_STYLECHANGED; | ||||
| 	TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); | ||||
| 	if (flags & SPAWN_BEFORE) | ||||
| 		TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry); | ||||
| 	else | ||||
| 		TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); | ||||
| 	layout_assign_pane(lc, src_wp); | ||||
|  | ||||
| 	recalculate_sizes(); | ||||
|   | ||||
| @@ -28,14 +28,14 @@ | ||||
|  */ | ||||
|  | ||||
| #define LIST_WINDOWS_TEMPLATE					\ | ||||
| 	"#{window_index}: #{window_name}#{window_flags} "	\ | ||||
| 	"#{window_index}: #{window_name}#{window_raw_flags} "	\ | ||||
| 	"(#{window_panes} panes) "				\ | ||||
| 	"[#{window_width}x#{window_height}] "			\ | ||||
| 	"[layout #{window_layout}] #{window_id}"		\ | ||||
| 	"#{?window_active, (active),}"; | ||||
| #define LIST_WINDOWS_WITH_SESSION_TEMPLATE			\ | ||||
| 	"#{session_name}:"					\ | ||||
| 	"#{window_index}: #{window_name}#{window_flags} "	\ | ||||
| 	"#{window_index}: #{window_name}#{window_raw_flags} "	\ | ||||
| 	"(#{window_panes} panes) "				\ | ||||
| 	"[#{window_width}x#{window_height}] " | ||||
|  | ||||
|   | ||||
| @@ -38,8 +38,8 @@ const struct cmd_entry cmd_new_window_entry = { | ||||
| 	.name = "new-window", | ||||
| 	.alias = "neww", | ||||
|  | ||||
| 	.args = { "abc:de:F:kn:Pt:", 0, -1 }, | ||||
| 	.usage = "[-abdkP] [-c start-directory] [-e environment] [-F format] " | ||||
| 	.args = { "abc:de:F:kn:PSt:", 0, -1 }, | ||||
| 	.usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] " | ||||
| 		 "[-n window-name] " CMD_TARGET_WINDOW_USAGE " [command]", | ||||
|  | ||||
| 	.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX }, | ||||
| @@ -52,6 +52,7 @@ static enum cmd_retval | ||||
| cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) | ||||
| { | ||||
| 	struct args		*args = cmd_get_args(self); | ||||
| 	struct client		*c = cmdq_get_client(item); | ||||
| 	struct cmd_find_state	*current = cmdq_get_current(item); | ||||
| 	struct cmd_find_state	*target = cmdq_get_target(item); | ||||
| 	struct spawn_context	 sc; | ||||
| @@ -59,12 +60,41 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 	struct session		*s = target->s; | ||||
| 	struct winlink		*wl = target->wl; | ||||
| 	int			 idx = target->idx, before; | ||||
| 	struct winlink		*new_wl; | ||||
| 	struct winlink		*new_wl = NULL; | ||||
| 	char			*cause = NULL, *cp; | ||||
| 	const char		*template, *add; | ||||
| 	const char		*template, *add, *name; | ||||
| 	struct cmd_find_state	 fs; | ||||
| 	struct args_value	*value; | ||||
|  | ||||
| 	/* | ||||
| 	 * If -S and -n are given and -t is not and a single window with this | ||||
| 	 * name already exists, select it. | ||||
| 	 */ | ||||
| 	name = args_get(args, 'n'); | ||||
| 	if (args_has(args, 'S') && name != NULL && target->idx == -1) { | ||||
| 		RB_FOREACH(wl, winlinks, &s->windows) { | ||||
| 			if (strcmp(wl->window->name, name) != 0) | ||||
| 				continue; | ||||
| 			if (new_wl == NULL) { | ||||
| 				new_wl = wl; | ||||
| 				continue; | ||||
| 			} | ||||
| 			cmdq_error(item, "multiple windows named %s", name); | ||||
| 			return (CMD_RETURN_ERROR); | ||||
| 		} | ||||
| 		if (new_wl != NULL) { | ||||
| 			if (args_has(args, 'd')) | ||||
| 				return (CMD_RETURN_NORMAL); | ||||
| 			if (session_set_current(s, new_wl) == 0) | ||||
| 				server_redraw_session(s); | ||||
| 			if (c != NULL && c->session != NULL) | ||||
| 				s->curw->window->latest = c; | ||||
| 			recalculate_sizes(); | ||||
| 			return (CMD_RETURN_NORMAL); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	before = args_has(args, 'b'); | ||||
| 	if (args_has(args, 'a') || before) { | ||||
| 		idx = winlink_shuffle_up(s, wl, before); | ||||
|   | ||||
| @@ -1505,8 +1505,12 @@ yylex_token(int ch) | ||||
| 		    state == NONE) | ||||
| 			break; | ||||
|  | ||||
| 		/* Spaces and comments inside quotes after \n are removed. */ | ||||
| 		/* | ||||
| 		 * Spaces and comments inside quotes after \n are removed but | ||||
| 		 * the \n is left. | ||||
| 		 */ | ||||
| 		if (ch == '\n' && state != NONE) { | ||||
| 			yylex_append1(&buf, &len, '\n'); | ||||
| 			while ((ch = yylex_getc()) == ' ' || ch == '\t') | ||||
| 				/* nothing */; | ||||
| 			if (ch != '#') | ||||
|   | ||||
| @@ -768,7 +768,11 @@ cmdq_running(struct client *c) | ||||
| { | ||||
| 	struct cmdq_list	*queue = cmdq_get(c); | ||||
|  | ||||
| 	return (queue->item); | ||||
| 	if (queue->item == NULL) | ||||
|         return (NULL); | ||||
|     if (queue->item->flags & CMDQ_WAITING) | ||||
|         return (NULL); | ||||
|     return (queue->item); | ||||
| } | ||||
|  | ||||
| /* Print a guard line. */ | ||||
|   | ||||
| @@ -40,8 +40,8 @@ const struct cmd_entry cmd_run_shell_entry = { | ||||
| 	.name = "run-shell", | ||||
| 	.alias = "run", | ||||
|  | ||||
| 	.args = { "bd:t:", 0, 1 }, | ||||
| 	.usage = "[-b] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", | ||||
| 	.args = { "bd:Ct:", 0, 1 }, | ||||
| 	.usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", | ||||
|  | ||||
| 	.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, | ||||
|  | ||||
| @@ -50,13 +50,16 @@ const struct cmd_entry cmd_run_shell_entry = { | ||||
| }; | ||||
|  | ||||
| struct cmd_run_shell_data { | ||||
| 	struct client		*client; | ||||
| 	char			*cmd; | ||||
| 	int			 shell; | ||||
| 	char			*cwd; | ||||
| 	struct cmdq_item	*item; | ||||
| 	struct session		*s; | ||||
| 	int			 wp_id; | ||||
| 	struct event		 timer; | ||||
| 	int			 flags; | ||||
| 	struct cmd_parse_input	 pi; | ||||
| }; | ||||
|  | ||||
| static void | ||||
| @@ -93,49 +96,69 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 	struct args			*args = cmd_get_args(self); | ||||
| 	struct cmd_find_state		*target = cmdq_get_target(item); | ||||
| 	struct cmd_run_shell_data	*cdata; | ||||
| 	struct client			*tc = cmdq_get_target_client(item); | ||||
| 	struct session			*s = target->s; | ||||
| 	struct window_pane		*wp = target->wp; | ||||
| 	const char			*delay; | ||||
| 	double				 d; | ||||
| 	struct timeval			 tv; | ||||
| 	char				*end; | ||||
| 	int				 wait = !args_has(args, 'b'); | ||||
|  | ||||
| 	if ((delay = args_get(args, 'd')) != NULL) { | ||||
| 		d = strtod(delay, &end); | ||||
| 		if (*end != '\0') { | ||||
| 			cmdq_error(item, "invalid delay time: %s", delay); | ||||
| 			return (CMD_RETURN_ERROR); | ||||
| 		} | ||||
| 	} else if (args->argc == 0) | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
|  | ||||
| 	cdata = xcalloc(1, sizeof *cdata); | ||||
| 	if (args->argc != 0) | ||||
| 		cdata->cmd = format_single_from_target(item, args->argv[0]); | ||||
|  | ||||
| 	cdata->shell = !args_has(args, 'C'); | ||||
| 	if (!cdata->shell) { | ||||
| 		memset(&cdata->pi, 0, sizeof cdata->pi); | ||||
| 		cmd_get_source(self, &cdata->pi.file, &cdata->pi.line); | ||||
| 		if (wait) | ||||
| 			cdata->pi.item = item; | ||||
| 		cdata->pi.c = tc; | ||||
| 		cmd_find_copy_state(&cdata->pi.fs, target); | ||||
| 	} | ||||
|  | ||||
| 	if (args_has(args, 't') && wp != NULL) | ||||
| 		cdata->wp_id = wp->id; | ||||
| 	else | ||||
| 		cdata->wp_id = -1; | ||||
|  | ||||
| 	if (!args_has(args, 'b')) | ||||
| 	if (wait) { | ||||
| 		cdata->client = cmdq_get_client(item); | ||||
| 		cdata->item = item; | ||||
| 	else | ||||
| 	} else { | ||||
| 		cdata->client = tc; | ||||
| 		cdata->flags |= JOB_NOWAIT; | ||||
| 	} | ||||
| 	if (cdata->client != NULL) | ||||
| 		cdata->client->references++; | ||||
|  | ||||
| 	cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s)); | ||||
|  | ||||
| 	cdata->s = s; | ||||
| 	if (s != NULL) | ||||
| 		session_add_ref(s, __func__); | ||||
|  | ||||
| 	evtimer_set(&cdata->timer, cmd_run_shell_timer, cdata); | ||||
|  | ||||
| 	if ((delay = args_get(args, 'd')) != NULL) { | ||||
| 		d = strtod(delay, &end); | ||||
| 		if (*end != '\0') { | ||||
| 			cmdq_error(item, "invalid delay time: %s", delay); | ||||
| 			cmd_run_shell_free(cdata); | ||||
| 			return (CMD_RETURN_ERROR); | ||||
| 		} | ||||
| 	if (delay != NULL) { | ||||
| 		timerclear(&tv); | ||||
| 		tv.tv_sec = (time_t)d; | ||||
| 		tv.tv_usec = (d - (double)tv.tv_sec) * 1000000U; | ||||
| 		evtimer_add(&cdata->timer, &tv); | ||||
| 	} else | ||||
| 		cmd_run_shell_timer(-1, 0, cdata); | ||||
| 		event_active(&cdata->timer, EV_TIMEOUT, 1); | ||||
|  | ||||
| 	if (args_has(args, 'b')) | ||||
| 	if (!wait) | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
| 	return (CMD_RETURN_WAIT); | ||||
| } | ||||
| @@ -144,17 +167,37 @@ static void | ||||
| cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) | ||||
| { | ||||
| 	struct cmd_run_shell_data	*cdata = arg; | ||||
| 	struct client			*c = cdata->client; | ||||
| 	const char			*cmd = cdata->cmd; | ||||
| 	char				*error; | ||||
| 	struct cmdq_item		*item = cdata->item; | ||||
| 	enum cmd_parse_status		 status; | ||||
|  | ||||
| 	if (cdata->cmd != NULL) { | ||||
| 		if (job_run(cdata->cmd, cdata->s, cdata->cwd, NULL, | ||||
| 	if (cmd != NULL && cdata->shell) { | ||||
| 		if (job_run(cmd, 0, NULL, cdata->s, cdata->cwd, NULL, | ||||
| 		    cmd_run_shell_callback, cmd_run_shell_free, cdata, | ||||
| 		    cdata->flags, -1, -1) == NULL) | ||||
| 			cmd_run_shell_free(cdata); | ||||
| 	} else { | ||||
| 		if (cdata->item != NULL) | ||||
| 			cmdq_continue(cdata->item); | ||||
| 		cmd_run_shell_free(cdata); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (cmd != NULL) { | ||||
| 		if (item != NULL) { | ||||
| 			status = cmd_parse_and_insert(cmd, &cdata->pi, item, | ||||
| 			    cmdq_get_state(item), &error); | ||||
| 		} else { | ||||
| 			status = cmd_parse_and_append(cmd, &cdata->pi, c, NULL, | ||||
| 			    &error); | ||||
| 		} | ||||
| 		if (status == CMD_PARSE_ERROR) { | ||||
| 		       cmdq_error(cdata->item, "%s", error); | ||||
| 		       free(error); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (cdata->item != NULL) | ||||
| 		cmdq_continue(cdata->item); | ||||
| 	cmd_run_shell_free(cdata); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -215,6 +258,8 @@ cmd_run_shell_free(void *data) | ||||
| 	evtimer_del(&cdata->timer); | ||||
| 	if (cdata->s != NULL) | ||||
| 		session_remove_ref(cdata->s, __func__); | ||||
| 	if (cdata->client != NULL) | ||||
| 		server_client_unref(cdata->client); | ||||
| 	free(cdata->cwd); | ||||
| 	free(cdata->cmd); | ||||
| 	free(cdata); | ||||
|   | ||||
| @@ -108,7 +108,7 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 	if (args_has(args, 'a')) | ||||
| 		flags = O_APPEND; | ||||
| 	else | ||||
| 		flags = 0; | ||||
| 		flags = O_TRUNC; | ||||
| 	file_write(cmdq_get_client(item), path, flags, bufdata, bufsize, | ||||
| 	    cmd_save_buffer_done, item); | ||||
| 	free(path); | ||||
|   | ||||
| @@ -108,11 +108,15 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 			cmdq_error(item, "no last pane"); | ||||
| 			return (CMD_RETURN_ERROR); | ||||
| 		} | ||||
| 		if (args_has(args, 'e')) | ||||
| 		if (args_has(args, 'e')) { | ||||
| 			lastwp->flags &= ~PANE_INPUTOFF; | ||||
| 		else if (args_has(args, 'd')) | ||||
| 			server_redraw_window_borders(lastwp->window); | ||||
| 			server_status_window(lastwp->window); | ||||
| 		} else if (args_has(args, 'd')) { | ||||
| 			lastwp->flags |= PANE_INPUTOFF; | ||||
| 		else { | ||||
| 			server_redraw_window_borders(lastwp->window); | ||||
| 			server_status_window(lastwp->window); | ||||
| 		} else { | ||||
| 			if (window_push_zoom(w, args_has(args, 'Z'))) | ||||
| 				server_redraw_window(w); | ||||
| 			window_redraw_active_switch(w, lastwp); | ||||
| @@ -188,10 +192,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) | ||||
|  | ||||
| 	if (args_has(args, 'e')) { | ||||
| 		wp->flags &= ~PANE_INPUTOFF; | ||||
| 		server_redraw_window_borders(wp->window); | ||||
| 		server_status_window(wp->window); | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
| 	} | ||||
| 	if (args_has(args, 'd')) { | ||||
| 		wp->flags |= PANE_INPUTOFF; | ||||
| 		server_redraw_window_borders(wp->window); | ||||
| 		server_status_window(wp->window); | ||||
| 		return (CMD_RETURN_NORMAL); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -33,8 +33,8 @@ const struct cmd_entry cmd_set_option_entry = { | ||||
| 	.name = "set-option", | ||||
| 	.alias = "set", | ||||
|  | ||||
| 	.args = { "aFgopqst:uw", 1, 2 }, | ||||
| 	.usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]", | ||||
| 	.args = { "aFgopqst:uUw", 1, 2 }, | ||||
| 	.usage = "[-aFgopqsuUw] " CMD_TARGET_PANE_USAGE " option [value]", | ||||
|  | ||||
| 	.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, | ||||
|  | ||||
| @@ -74,8 +74,9 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 	struct args			*args = cmd_get_args(self); | ||||
| 	int				 append = args_has(args, 'a'); | ||||
| 	struct cmd_find_state		*target = cmdq_get_target(item); | ||||
| 	struct window_pane		*loop; | ||||
| 	struct options			*oo; | ||||
| 	struct options_entry		*parent, *o; | ||||
| 	struct options_entry		*parent, *o, *po; | ||||
| 	char				*name, *argument, *value = NULL, *cause; | ||||
| 	int				 window, idx, already, error, ambiguous; | ||||
| 	int				 scope; | ||||
| @@ -148,7 +149,19 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) | ||||
| 	} | ||||
|  | ||||
| 	/* Change the option. */ | ||||
| 	if (args_has(args, 'u')) { | ||||
| 	if (args_has(args, 'U') && scope == OPTIONS_TABLE_WINDOW) { | ||||
| 		TAILQ_FOREACH(loop, &target->w->panes, entry) { | ||||
| 			po = options_get_only(loop->options, name); | ||||
| 			if (po == NULL) | ||||
| 				continue; | ||||
| 			if (options_remove_or_default(po, idx, &cause) != 0) { | ||||
| 				cmdq_error(item, "%s", cause); | ||||
| 				free(cause); | ||||
| 				goto fail; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if (args_has(args, 'u') || args_has(args, 'U')) { | ||||
| 		if (o == NULL) | ||||
| 			goto out; | ||||
| 		if (options_remove_or_default(o, idx, &cause) != 0) { | ||||
|   | ||||
| @@ -200,11 +200,13 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item, int scope, | ||||
| 	u_int					 idx; | ||||
| 	int					 parent; | ||||
|  | ||||
| 	o = options_first(oo); | ||||
| 	while (o != NULL) { | ||||
| 		if (options_table_entry(o) == NULL) | ||||
| 			cmd_show_options_print(self, item, o, -1, 0); | ||||
| 		o = options_next(o); | ||||
| 	if (cmd_get_entry(self) != &cmd_show_hooks_entry) { | ||||
| 		o = options_first(oo); | ||||
| 		while (o != NULL) { | ||||
| 			if (options_table_entry(o) == NULL) | ||||
| 				cmd_show_options_print(self, item, o, -1, 0); | ||||
| 			o = options_next(o); | ||||
| 		} | ||||
| 	} | ||||
| 	for (oe = options_table; oe->name != NULL; oe++) { | ||||
| 		if (~oe->scope & scope) | ||||
|   | ||||
							
								
								
									
										611
									
								
								colour.c
									
									
									
									
									
								
							
							
						
						
									
										611
									
								
								colour.c
									
									
									
									
									
								
							| @@ -22,6 +22,7 @@ | ||||
| #include <ctype.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <math.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| @@ -111,6 +112,9 @@ colour_tostring(int c) | ||||
| 	static char	s[32]; | ||||
| 	u_char		r, g, b; | ||||
|  | ||||
| 	if (c == -1) | ||||
| 		return ("invalid"); | ||||
|  | ||||
| 	if (c & COLOUR_FLAG_RGB) { | ||||
| 		colour_split_rgb(c, &r, &g, &b); | ||||
| 		xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b); | ||||
| @@ -233,7 +237,7 @@ colour_fromstring(const char *s) | ||||
| 		return (96); | ||||
| 	if (strcasecmp(s, "brightwhite") == 0 || strcmp(s, "97") == 0) | ||||
| 		return (97); | ||||
| 	return (-1); | ||||
| 	return (colour_byname(s)); | ||||
| } | ||||
|  | ||||
| /* Convert 256 colour to RGB colour. */ | ||||
| @@ -335,3 +339,608 @@ colour_256to16(int c) | ||||
|  | ||||
| 	return (table[c & 0xff]); | ||||
| } | ||||
|  | ||||
| /* Get colour by X11 colour name. */ | ||||
| int | ||||
| colour_byname(const char *name) | ||||
| { | ||||
| 	static const struct { | ||||
| 		const char	*name; | ||||
| 		int		 c; | ||||
| 	} colours[] = { | ||||
| 		{ "AliceBlue", 0xf0f8ff }, | ||||
| 		{ "AntiqueWhite", 0xfaebd7 }, | ||||
| 		{ "AntiqueWhite1", 0xffefdb }, | ||||
| 		{ "AntiqueWhite2", 0xeedfcc }, | ||||
| 		{ "AntiqueWhite3", 0xcdc0b0 }, | ||||
| 		{ "AntiqueWhite4", 0x8b8378 }, | ||||
| 		{ "BlanchedAlmond", 0xffebcd }, | ||||
| 		{ "BlueViolet", 0x8a2be2 }, | ||||
| 		{ "CadetBlue", 0x5f9ea0 }, | ||||
| 		{ "CadetBlue1", 0x98f5ff }, | ||||
| 		{ "CadetBlue2", 0x8ee5ee }, | ||||
| 		{ "CadetBlue3", 0x7ac5cd }, | ||||
| 		{ "CadetBlue4", 0x53868b }, | ||||
| 		{ "CornflowerBlue", 0x6495ed }, | ||||
| 		{ "DarkBlue", 0x00008b }, | ||||
| 		{ "DarkCyan", 0x008b8b }, | ||||
| 		{ "DarkGoldenrod", 0xb8860b }, | ||||
| 		{ "DarkGoldenrod1", 0xffb90f }, | ||||
| 		{ "DarkGoldenrod2", 0xeead0e }, | ||||
| 		{ "DarkGoldenrod3", 0xcd950c }, | ||||
| 		{ "DarkGoldenrod4", 0x8b6508 }, | ||||
| 		{ "DarkGray", 0xa9a9a9 }, | ||||
| 		{ "DarkGreen", 0x006400 }, | ||||
| 		{ "DarkGrey", 0xa9a9a9 }, | ||||
| 		{ "DarkKhaki", 0xbdb76b }, | ||||
| 		{ "DarkMagenta", 0x8b008b }, | ||||
| 		{ "DarkOliveGreen", 0x556b2f }, | ||||
| 		{ "DarkOliveGreen1", 0xcaff70 }, | ||||
| 		{ "DarkOliveGreen2", 0xbcee68 }, | ||||
| 		{ "DarkOliveGreen3", 0xa2cd5a }, | ||||
| 		{ "DarkOliveGreen4", 0x6e8b3d }, | ||||
| 		{ "DarkOrange", 0xff8c00 }, | ||||
| 		{ "DarkOrange1", 0xff7f00 }, | ||||
| 		{ "DarkOrange2", 0xee7600 }, | ||||
| 		{ "DarkOrange3", 0xcd6600 }, | ||||
| 		{ "DarkOrange4", 0x8b4500 }, | ||||
| 		{ "DarkOrchid", 0x9932cc }, | ||||
| 		{ "DarkOrchid1", 0xbf3eff }, | ||||
| 		{ "DarkOrchid2", 0xb23aee }, | ||||
| 		{ "DarkOrchid3", 0x9a32cd }, | ||||
| 		{ "DarkOrchid4", 0x68228b }, | ||||
| 		{ "DarkRed", 0x8b0000 }, | ||||
| 		{ "DarkSalmon", 0xe9967a }, | ||||
| 		{ "DarkSeaGreen", 0x8fbc8f }, | ||||
| 		{ "DarkSeaGreen1", 0xc1ffc1 }, | ||||
| 		{ "DarkSeaGreen2", 0xb4eeb4 }, | ||||
| 		{ "DarkSeaGreen3", 0x9bcd9b }, | ||||
| 		{ "DarkSeaGreen4", 0x698b69 }, | ||||
| 		{ "DarkSlateBlue", 0x483d8b }, | ||||
| 		{ "DarkSlateGray", 0x2f4f4f }, | ||||
| 		{ "DarkSlateGray1", 0x97ffff }, | ||||
| 		{ "DarkSlateGray2", 0x8deeee }, | ||||
| 		{ "DarkSlateGray3", 0x79cdcd }, | ||||
| 		{ "DarkSlateGray4", 0x528b8b }, | ||||
| 		{ "DarkSlateGrey", 0x2f4f4f }, | ||||
| 		{ "DarkTurquoise", 0x00ced1 }, | ||||
| 		{ "DarkViolet", 0x9400d3 }, | ||||
| 		{ "DeepPink", 0xff1493 }, | ||||
| 		{ "DeepPink1", 0xff1493 }, | ||||
| 		{ "DeepPink2", 0xee1289 }, | ||||
| 		{ "DeepPink3", 0xcd1076 }, | ||||
| 		{ "DeepPink4", 0x8b0a50 }, | ||||
| 		{ "DeepSkyBlue", 0x00bfff }, | ||||
| 		{ "DeepSkyBlue1", 0x00bfff }, | ||||
| 		{ "DeepSkyBlue2", 0x00b2ee }, | ||||
| 		{ "DeepSkyBlue3", 0x009acd }, | ||||
| 		{ "DeepSkyBlue4", 0x00688b }, | ||||
| 		{ "DimGray", 0x696969 }, | ||||
| 		{ "DimGrey", 0x696969 }, | ||||
| 		{ "DodgerBlue", 0x1e90ff }, | ||||
| 		{ "DodgerBlue1", 0x1e90ff }, | ||||
| 		{ "DodgerBlue2", 0x1c86ee }, | ||||
| 		{ "DodgerBlue3", 0x1874cd }, | ||||
| 		{ "DodgerBlue4", 0x104e8b }, | ||||
| 		{ "FloralWhite", 0xfffaf0 }, | ||||
| 		{ "ForestGreen", 0x228b22 }, | ||||
| 		{ "GhostWhite", 0xf8f8ff }, | ||||
| 		{ "GreenYellow", 0xadff2f }, | ||||
| 		{ "HotPink", 0xff69b4 }, | ||||
| 		{ "HotPink1", 0xff6eb4 }, | ||||
| 		{ "HotPink2", 0xee6aa7 }, | ||||
| 		{ "HotPink3", 0xcd6090 }, | ||||
| 		{ "HotPink4", 0x8b3a62 }, | ||||
| 		{ "IndianRed", 0xcd5c5c }, | ||||
| 		{ "IndianRed1", 0xff6a6a }, | ||||
| 		{ "IndianRed2", 0xee6363 }, | ||||
| 		{ "IndianRed3", 0xcd5555 }, | ||||
| 		{ "IndianRed4", 0x8b3a3a }, | ||||
| 		{ "LavenderBlush", 0xfff0f5 }, | ||||
| 		{ "LavenderBlush1", 0xfff0f5 }, | ||||
| 		{ "LavenderBlush2", 0xeee0e5 }, | ||||
| 		{ "LavenderBlush3", 0xcdc1c5 }, | ||||
| 		{ "LavenderBlush4", 0x8b8386 }, | ||||
| 		{ "LawnGreen", 0x7cfc00 }, | ||||
| 		{ "LemonChiffon", 0xfffacd }, | ||||
| 		{ "LemonChiffon1", 0xfffacd }, | ||||
| 		{ "LemonChiffon2", 0xeee9bf }, | ||||
| 		{ "LemonChiffon3", 0xcdc9a5 }, | ||||
| 		{ "LemonChiffon4", 0x8b8970 }, | ||||
| 		{ "LightBlue", 0xadd8e6 }, | ||||
| 		{ "LightBlue1", 0xbfefff }, | ||||
| 		{ "LightBlue2", 0xb2dfee }, | ||||
| 		{ "LightBlue3", 0x9ac0cd }, | ||||
| 		{ "LightBlue4", 0x68838b }, | ||||
| 		{ "LightCoral", 0xf08080 }, | ||||
| 		{ "LightCyan", 0xe0ffff }, | ||||
| 		{ "LightCyan1", 0xe0ffff }, | ||||
| 		{ "LightCyan2", 0xd1eeee }, | ||||
| 		{ "LightCyan3", 0xb4cdcd }, | ||||
| 		{ "LightCyan4", 0x7a8b8b }, | ||||
| 		{ "LightGoldenrod", 0xeedd82 }, | ||||
| 		{ "LightGoldenrod1", 0xffec8b }, | ||||
| 		{ "LightGoldenrod2", 0xeedc82 }, | ||||
| 		{ "LightGoldenrod3", 0xcdbe70 }, | ||||
| 		{ "LightGoldenrod4", 0x8b814c }, | ||||
| 		{ "LightGoldenrodYellow", 0xfafad2 }, | ||||
| 		{ "LightGray", 0xd3d3d3 }, | ||||
| 		{ "LightGreen", 0x90ee90 }, | ||||
| 		{ "LightGrey", 0xd3d3d3 }, | ||||
| 		{ "LightPink", 0xffb6c1 }, | ||||
| 		{ "LightPink1", 0xffaeb9 }, | ||||
| 		{ "LightPink2", 0xeea2ad }, | ||||
| 		{ "LightPink3", 0xcd8c95 }, | ||||
| 		{ "LightPink4", 0x8b5f65 }, | ||||
| 		{ "LightSalmon", 0xffa07a }, | ||||
| 		{ "LightSalmon1", 0xffa07a }, | ||||
| 		{ "LightSalmon2", 0xee9572 }, | ||||
| 		{ "LightSalmon3", 0xcd8162 }, | ||||
| 		{ "LightSalmon4", 0x8b5742 }, | ||||
| 		{ "LightSeaGreen", 0x20b2aa }, | ||||
| 		{ "LightSkyBlue", 0x87cefa }, | ||||
| 		{ "LightSkyBlue1", 0xb0e2ff }, | ||||
| 		{ "LightSkyBlue2", 0xa4d3ee }, | ||||
| 		{ "LightSkyBlue3", 0x8db6cd }, | ||||
| 		{ "LightSkyBlue4", 0x607b8b }, | ||||
| 		{ "LightSlateBlue", 0x8470ff }, | ||||
| 		{ "LightSlateGray", 0x778899 }, | ||||
| 		{ "LightSlateGrey", 0x778899 }, | ||||
| 		{ "LightSteelBlue", 0xb0c4de }, | ||||
| 		{ "LightSteelBlue1", 0xcae1ff }, | ||||
| 		{ "LightSteelBlue2", 0xbcd2ee }, | ||||
| 		{ "LightSteelBlue3", 0xa2b5cd }, | ||||
| 		{ "LightSteelBlue4", 0x6e7b8b }, | ||||
| 		{ "LightYellow", 0xffffe0 }, | ||||
| 		{ "LightYellow1", 0xffffe0 }, | ||||
| 		{ "LightYellow2", 0xeeeed1 }, | ||||
| 		{ "LightYellow3", 0xcdcdb4 }, | ||||
| 		{ "LightYellow4", 0x8b8b7a }, | ||||
| 		{ "LimeGreen", 0x32cd32 }, | ||||
| 		{ "MediumAquamarine", 0x66cdaa }, | ||||
| 		{ "MediumBlue", 0x0000cd }, | ||||
| 		{ "MediumOrchid", 0xba55d3 }, | ||||
| 		{ "MediumOrchid1", 0xe066ff }, | ||||
| 		{ "MediumOrchid2", 0xd15fee }, | ||||
| 		{ "MediumOrchid3", 0xb452cd }, | ||||
| 		{ "MediumOrchid4", 0x7a378b }, | ||||
| 		{ "MediumPurple", 0x9370db }, | ||||
| 		{ "MediumPurple1", 0xab82ff }, | ||||
| 		{ "MediumPurple2", 0x9f79ee }, | ||||
| 		{ "MediumPurple3", 0x8968cd }, | ||||
| 		{ "MediumPurple4", 0x5d478b }, | ||||
| 		{ "MediumSeaGreen", 0x3cb371 }, | ||||
| 		{ "MediumSlateBlue", 0x7b68ee }, | ||||
| 		{ "MediumSpringGreen", 0x00fa9a }, | ||||
| 		{ "MediumTurquoise", 0x48d1cc }, | ||||
| 		{ "MediumVioletRed", 0xc71585 }, | ||||
| 		{ "MidnightBlue", 0x191970 }, | ||||
| 		{ "MintCream", 0xf5fffa }, | ||||
| 		{ "MistyRose", 0xffe4e1 }, | ||||
| 		{ "MistyRose1", 0xffe4e1 }, | ||||
| 		{ "MistyRose2", 0xeed5d2 }, | ||||
| 		{ "MistyRose3", 0xcdb7b5 }, | ||||
| 		{ "MistyRose4", 0x8b7d7b }, | ||||
| 		{ "NavajoWhite", 0xffdead }, | ||||
| 		{ "NavajoWhite1", 0xffdead }, | ||||
| 		{ "NavajoWhite2", 0xeecfa1 }, | ||||
| 		{ "NavajoWhite3", 0xcdb38b }, | ||||
| 		{ "NavajoWhite4", 0x8b795e }, | ||||
| 		{ "NavyBlue", 0x000080 }, | ||||
| 		{ "OldLace", 0xfdf5e6 }, | ||||
| 		{ "OliveDrab", 0x6b8e23 }, | ||||
| 		{ "OliveDrab1", 0xc0ff3e }, | ||||
| 		{ "OliveDrab2", 0xb3ee3a }, | ||||
| 		{ "OliveDrab3", 0x9acd32 }, | ||||
| 		{ "OliveDrab4", 0x698b22 }, | ||||
| 		{ "OrangeRed", 0xff4500 }, | ||||
| 		{ "OrangeRed1", 0xff4500 }, | ||||
| 		{ "OrangeRed2", 0xee4000 }, | ||||
| 		{ "OrangeRed3", 0xcd3700 }, | ||||
| 		{ "OrangeRed4", 0x8b2500 }, | ||||
| 		{ "PaleGoldenrod", 0xeee8aa }, | ||||
| 		{ "PaleGreen", 0x98fb98 }, | ||||
| 		{ "PaleGreen1", 0x9aff9a }, | ||||
| 		{ "PaleGreen2", 0x90ee90 }, | ||||
| 		{ "PaleGreen3", 0x7ccd7c }, | ||||
| 		{ "PaleGreen4", 0x548b54 }, | ||||
| 		{ "PaleTurquoise", 0xafeeee }, | ||||
| 		{ "PaleTurquoise1", 0xbbffff }, | ||||
| 		{ "PaleTurquoise2", 0xaeeeee }, | ||||
| 		{ "PaleTurquoise3", 0x96cdcd }, | ||||
| 		{ "PaleTurquoise4", 0x668b8b }, | ||||
| 		{ "PaleVioletRed", 0xdb7093 }, | ||||
| 		{ "PaleVioletRed1", 0xff82ab }, | ||||
| 		{ "PaleVioletRed2", 0xee799f }, | ||||
| 		{ "PaleVioletRed3", 0xcd6889 }, | ||||
| 		{ "PaleVioletRed4", 0x8b475d }, | ||||
| 		{ "PapayaWhip", 0xffefd5 }, | ||||
| 		{ "PeachPuff", 0xffdab9 }, | ||||
| 		{ "PeachPuff1", 0xffdab9 }, | ||||
| 		{ "PeachPuff2", 0xeecbad }, | ||||
| 		{ "PeachPuff3", 0xcdaf95 }, | ||||
| 		{ "PeachPuff4", 0x8b7765 }, | ||||
| 		{ "PowderBlue", 0xb0e0e6 }, | ||||
| 		{ "RebeccaPurple", 0x663399 }, | ||||
| 		{ "RosyBrown", 0xbc8f8f }, | ||||
| 		{ "RosyBrown1", 0xffc1c1 }, | ||||
| 		{ "RosyBrown2", 0xeeb4b4 }, | ||||
| 		{ "RosyBrown3", 0xcd9b9b }, | ||||
| 		{ "RosyBrown4", 0x8b6969 }, | ||||
| 		{ "RoyalBlue", 0x4169e1 }, | ||||
| 		{ "RoyalBlue1", 0x4876ff }, | ||||
| 		{ "RoyalBlue2", 0x436eee }, | ||||
| 		{ "RoyalBlue3", 0x3a5fcd }, | ||||
| 		{ "RoyalBlue4", 0x27408b }, | ||||
| 		{ "SaddleBrown", 0x8b4513 }, | ||||
| 		{ "SandyBrown", 0xf4a460 }, | ||||
| 		{ "SeaGreen", 0x2e8b57 }, | ||||
| 		{ "SeaGreen1", 0x54ff9f }, | ||||
| 		{ "SeaGreen2", 0x4eee94 }, | ||||
| 		{ "SeaGreen3", 0x43cd80 }, | ||||
| 		{ "SeaGreen4", 0x2e8b57 }, | ||||
| 		{ "SkyBlue", 0x87ceeb }, | ||||
| 		{ "SkyBlue1", 0x87ceff }, | ||||
| 		{ "SkyBlue2", 0x7ec0ee }, | ||||
| 		{ "SkyBlue3", 0x6ca6cd }, | ||||
| 		{ "SkyBlue4", 0x4a708b }, | ||||
| 		{ "SlateBlue", 0x6a5acd }, | ||||
| 		{ "SlateBlue1", 0x836fff }, | ||||
| 		{ "SlateBlue2", 0x7a67ee }, | ||||
| 		{ "SlateBlue3", 0x6959cd }, | ||||
| 		{ "SlateBlue4", 0x473c8b }, | ||||
| 		{ "SlateGray", 0x708090 }, | ||||
| 		{ "SlateGray1", 0xc6e2ff }, | ||||
| 		{ "SlateGray2", 0xb9d3ee }, | ||||
| 		{ "SlateGray3", 0x9fb6cd }, | ||||
| 		{ "SlateGray4", 0x6c7b8b }, | ||||
| 		{ "SlateGrey", 0x708090 }, | ||||
| 		{ "SpringGreen", 0x00ff7f }, | ||||
| 		{ "SpringGreen1", 0x00ff7f }, | ||||
| 		{ "SpringGreen2", 0x00ee76 }, | ||||
| 		{ "SpringGreen3", 0x00cd66 }, | ||||
| 		{ "SpringGreen4", 0x008b45 }, | ||||
| 		{ "SteelBlue", 0x4682b4 }, | ||||
| 		{ "SteelBlue1", 0x63b8ff }, | ||||
| 		{ "SteelBlue2", 0x5cacee }, | ||||
| 		{ "SteelBlue3", 0x4f94cd }, | ||||
| 		{ "SteelBlue4", 0x36648b }, | ||||
| 		{ "VioletRed", 0xd02090 }, | ||||
| 		{ "VioletRed1", 0xff3e96 }, | ||||
| 		{ "VioletRed2", 0xee3a8c }, | ||||
| 		{ "VioletRed3", 0xcd3278 }, | ||||
| 		{ "VioletRed4", 0x8b2252 }, | ||||
| 		{ "WebGray", 0x808080 }, | ||||
| 		{ "WebGreen", 0x008000 }, | ||||
| 		{ "WebGrey", 0x808080 }, | ||||
| 		{ "WebMaroon", 0x800000 }, | ||||
| 		{ "WebPurple", 0x800080 }, | ||||
| 		{ "WhiteSmoke", 0xf5f5f5 }, | ||||
| 		{ "X11Gray", 0xbebebe }, | ||||
| 		{ "X11Green", 0x00ff00 }, | ||||
| 		{ "X11Grey", 0xbebebe }, | ||||
| 		{ "X11Maroon", 0xb03060 }, | ||||
| 		{ "X11Purple", 0xa020f0 }, | ||||
| 		{ "YellowGreen", 0x9acd32 }, | ||||
| 		{ "alice blue", 0xf0f8ff }, | ||||
| 		{ "antique white", 0xfaebd7 }, | ||||
| 		{ "aqua", 0x00ffff }, | ||||
| 		{ "aquamarine", 0x7fffd4 }, | ||||
| 		{ "aquamarine1", 0x7fffd4 }, | ||||
| 		{ "aquamarine2", 0x76eec6 }, | ||||
| 		{ "aquamarine3", 0x66cdaa }, | ||||
| 		{ "aquamarine4", 0x458b74 }, | ||||
| 		{ "azure", 0xf0ffff }, | ||||
| 		{ "azure1", 0xf0ffff }, | ||||
| 		{ "azure2", 0xe0eeee }, | ||||
| 		{ "azure3", 0xc1cdcd }, | ||||
| 		{ "azure4", 0x838b8b }, | ||||
| 		{ "beige", 0xf5f5dc }, | ||||
| 		{ "bisque", 0xffe4c4 }, | ||||
| 		{ "bisque1", 0xffe4c4 }, | ||||
| 		{ "bisque2", 0xeed5b7 }, | ||||
| 		{ "bisque3", 0xcdb79e }, | ||||
| 		{ "bisque4", 0x8b7d6b }, | ||||
| 		{ "black", 0x000000 }, | ||||
| 		{ "blanched almond", 0xffebcd }, | ||||
| 		{ "blue violet", 0x8a2be2 }, | ||||
| 		{ "blue", 0x0000ff }, | ||||
| 		{ "blue1", 0x0000ff }, | ||||
| 		{ "blue2", 0x0000ee }, | ||||
| 		{ "blue3", 0x0000cd }, | ||||
| 		{ "blue4", 0x00008b }, | ||||
| 		{ "brown", 0xa52a2a }, | ||||
| 		{ "brown1", 0xff4040 }, | ||||
| 		{ "brown2", 0xee3b3b }, | ||||
| 		{ "brown3", 0xcd3333 }, | ||||
| 		{ "brown4", 0x8b2323 }, | ||||
| 		{ "burlywood", 0xdeb887 }, | ||||
| 		{ "burlywood1", 0xffd39b }, | ||||
| 		{ "burlywood2", 0xeec591 }, | ||||
| 		{ "burlywood3", 0xcdaa7d }, | ||||
| 		{ "burlywood4", 0x8b7355 }, | ||||
| 		{ "cadet blue", 0x5f9ea0 }, | ||||
| 		{ "chartreuse", 0x7fff00 }, | ||||
| 		{ "chartreuse1", 0x7fff00 }, | ||||
| 		{ "chartreuse2", 0x76ee00 }, | ||||
| 		{ "chartreuse3", 0x66cd00 }, | ||||
| 		{ "chartreuse4", 0x458b00 }, | ||||
| 		{ "chocolate", 0xd2691e }, | ||||
| 		{ "chocolate1", 0xff7f24 }, | ||||
| 		{ "chocolate2", 0xee7621 }, | ||||
| 		{ "chocolate3", 0xcd661d }, | ||||
| 		{ "chocolate4", 0x8b4513 }, | ||||
| 		{ "coral", 0xff7f50 }, | ||||
| 		{ "coral1", 0xff7256 }, | ||||
| 		{ "coral2", 0xee6a50 }, | ||||
| 		{ "coral3", 0xcd5b45 }, | ||||
| 		{ "coral4", 0x8b3e2f }, | ||||
| 		{ "cornflower blue", 0x6495ed }, | ||||
| 		{ "cornsilk", 0xfff8dc }, | ||||
| 		{ "cornsilk1", 0xfff8dc }, | ||||
| 		{ "cornsilk2", 0xeee8cd }, | ||||
| 		{ "cornsilk3", 0xcdc8b1 }, | ||||
| 		{ "cornsilk4", 0x8b8878 }, | ||||
| 		{ "crimson", 0xdc143c }, | ||||
| 		{ "cyan", 0x00ffff }, | ||||
| 		{ "cyan1", 0x00ffff }, | ||||
| 		{ "cyan2", 0x00eeee }, | ||||
| 		{ "cyan3", 0x00cdcd }, | ||||
| 		{ "cyan4", 0x008b8b }, | ||||
| 		{ "dark blue", 0x00008b }, | ||||
| 		{ "dark cyan", 0x008b8b }, | ||||
| 		{ "dark goldenrod", 0xb8860b }, | ||||
| 		{ "dark gray", 0xa9a9a9 }, | ||||
| 		{ "dark green", 0x006400 }, | ||||
| 		{ "dark grey", 0xa9a9a9 }, | ||||
| 		{ "dark khaki", 0xbdb76b }, | ||||
| 		{ "dark magenta", 0x8b008b }, | ||||
| 		{ "dark olive green", 0x556b2f }, | ||||
| 		{ "dark orange", 0xff8c00 }, | ||||
| 		{ "dark orchid", 0x9932cc }, | ||||
| 		{ "dark red", 0x8b0000 }, | ||||
| 		{ "dark salmon", 0xe9967a }, | ||||
| 		{ "dark sea green", 0x8fbc8f }, | ||||
| 		{ "dark slate blue", 0x483d8b }, | ||||
| 		{ "dark slate gray", 0x2f4f4f }, | ||||
| 		{ "dark slate grey", 0x2f4f4f }, | ||||
| 		{ "dark turquoise", 0x00ced1 }, | ||||
| 		{ "dark violet", 0x9400d3 }, | ||||
| 		{ "deep pink", 0xff1493 }, | ||||
| 		{ "deep sky blue", 0x00bfff }, | ||||
| 		{ "dim gray", 0x696969 }, | ||||
| 		{ "dim grey", 0x696969 }, | ||||
| 		{ "dodger blue", 0x1e90ff }, | ||||
| 		{ "firebrick", 0xb22222 }, | ||||
| 		{ "firebrick1", 0xff3030 }, | ||||
| 		{ "firebrick2", 0xee2c2c }, | ||||
| 		{ "firebrick3", 0xcd2626 }, | ||||
| 		{ "firebrick4", 0x8b1a1a }, | ||||
| 		{ "floral white", 0xfffaf0 }, | ||||
| 		{ "forest green", 0x228b22 }, | ||||
| 		{ "fuchsia", 0xff00ff }, | ||||
| 		{ "gainsboro", 0xdcdcdc }, | ||||
| 		{ "ghost white", 0xf8f8ff }, | ||||
| 		{ "gold", 0xffd700 }, | ||||
| 		{ "gold1", 0xffd700 }, | ||||
| 		{ "gold2", 0xeec900 }, | ||||
| 		{ "gold3", 0xcdad00 }, | ||||
| 		{ "gold4", 0x8b7500 }, | ||||
| 		{ "goldenrod", 0xdaa520 }, | ||||
| 		{ "goldenrod1", 0xffc125 }, | ||||
| 		{ "goldenrod2", 0xeeb422 }, | ||||
| 		{ "goldenrod3", 0xcd9b1d }, | ||||
| 		{ "goldenrod4", 0x8b6914 }, | ||||
| 		{ "green yellow", 0xadff2f }, | ||||
| 		{ "green", 0x00ff00 }, | ||||
| 		{ "green1", 0x00ff00 }, | ||||
| 		{ "green2", 0x00ee00 }, | ||||
| 		{ "green3", 0x00cd00 }, | ||||
| 		{ "green4", 0x008b00 }, | ||||
| 		{ "honeydew", 0xf0fff0 }, | ||||
| 		{ "honeydew1", 0xf0fff0 }, | ||||
| 		{ "honeydew2", 0xe0eee0 }, | ||||
| 		{ "honeydew3", 0xc1cdc1 }, | ||||
| 		{ "honeydew4", 0x838b83 }, | ||||
| 		{ "hot pink", 0xff69b4 }, | ||||
| 		{ "indian red", 0xcd5c5c }, | ||||
| 		{ "indigo", 0x4b0082 }, | ||||
| 		{ "ivory", 0xfffff0 }, | ||||
| 		{ "ivory1", 0xfffff0 }, | ||||
| 		{ "ivory2", 0xeeeee0 }, | ||||
| 		{ "ivory3", 0xcdcdc1 }, | ||||
| 		{ "ivory4", 0x8b8b83 }, | ||||
| 		{ "khaki", 0xf0e68c }, | ||||
| 		{ "khaki1", 0xfff68f }, | ||||
| 		{ "khaki2", 0xeee685 }, | ||||
| 		{ "khaki3", 0xcdc673 }, | ||||
| 		{ "khaki4", 0x8b864e }, | ||||
| 		{ "lavender blush", 0xfff0f5 }, | ||||
| 		{ "lavender", 0xe6e6fa }, | ||||
| 		{ "lawn green", 0x7cfc00 }, | ||||
| 		{ "lemon chiffon", 0xfffacd }, | ||||
| 		{ "light blue", 0xadd8e6 }, | ||||
| 		{ "light coral", 0xf08080 }, | ||||
| 		{ "light cyan", 0xe0ffff }, | ||||
| 		{ "light goldenrod yellow", 0xfafad2 }, | ||||
| 		{ "light goldenrod", 0xeedd82 }, | ||||
| 		{ "light gray", 0xd3d3d3 }, | ||||
| 		{ "light green", 0x90ee90 }, | ||||
| 		{ "light grey", 0xd3d3d3 }, | ||||
| 		{ "light pink", 0xffb6c1 }, | ||||
| 		{ "light salmon", 0xffa07a }, | ||||
| 		{ "light sea green", 0x20b2aa }, | ||||
| 		{ "light sky blue", 0x87cefa }, | ||||
| 		{ "light slate blue", 0x8470ff }, | ||||
| 		{ "light slate gray", 0x778899 }, | ||||
| 		{ "light slate grey", 0x778899 }, | ||||
| 		{ "light steel blue", 0xb0c4de }, | ||||
| 		{ "light yellow", 0xffffe0 }, | ||||
| 		{ "lime green", 0x32cd32 }, | ||||
| 		{ "lime", 0x00ff00 }, | ||||
| 		{ "linen", 0xfaf0e6 }, | ||||
| 		{ "magenta", 0xff00ff }, | ||||
| 		{ "magenta1", 0xff00ff }, | ||||
| 		{ "magenta2", 0xee00ee }, | ||||
| 		{ "magenta3", 0xcd00cd }, | ||||
| 		{ "magenta4", 0x8b008b }, | ||||
| 		{ "maroon", 0xb03060 }, | ||||
| 		{ "maroon1", 0xff34b3 }, | ||||
| 		{ "maroon2", 0xee30a7 }, | ||||
| 		{ "maroon3", 0xcd2990 }, | ||||
| 		{ "maroon4", 0x8b1c62 }, | ||||
| 		{ "medium aquamarine", 0x66cdaa }, | ||||
| 		{ "medium blue", 0x0000cd }, | ||||
| 		{ "medium orchid", 0xba55d3 }, | ||||
| 		{ "medium purple", 0x9370db }, | ||||
| 		{ "medium sea green", 0x3cb371 }, | ||||
| 		{ "medium slate blue", 0x7b68ee }, | ||||
| 		{ "medium spring green", 0x00fa9a }, | ||||
| 		{ "medium turquoise", 0x48d1cc }, | ||||
| 		{ "medium violet red", 0xc71585 }, | ||||
| 		{ "midnight blue", 0x191970 }, | ||||
| 		{ "mint cream", 0xf5fffa }, | ||||
| 		{ "misty rose", 0xffe4e1 }, | ||||
| 		{ "moccasin", 0xffe4b5 }, | ||||
| 		{ "navajo white", 0xffdead }, | ||||
| 		{ "navy blue", 0x000080 }, | ||||
| 		{ "navy", 0x000080 }, | ||||
| 		{ "old lace", 0xfdf5e6 }, | ||||
| 		{ "olive drab", 0x6b8e23 }, | ||||
| 		{ "olive", 0x808000 }, | ||||
| 		{ "orange red", 0xff4500 }, | ||||
| 		{ "orange", 0xffa500 }, | ||||
| 		{ "orange1", 0xffa500 }, | ||||
| 		{ "orange2", 0xee9a00 }, | ||||
| 		{ "orange3", 0xcd8500 }, | ||||
| 		{ "orange4", 0x8b5a00 }, | ||||
| 		{ "orchid", 0xda70d6 }, | ||||
| 		{ "orchid1", 0xff83fa }, | ||||
| 		{ "orchid2", 0xee7ae9 }, | ||||
| 		{ "orchid3", 0xcd69c9 }, | ||||
| 		{ "orchid4", 0x8b4789 }, | ||||
| 		{ "pale goldenrod", 0xeee8aa }, | ||||
| 		{ "pale green", 0x98fb98 }, | ||||
| 		{ "pale turquoise", 0xafeeee }, | ||||
| 		{ "pale violet red", 0xdb7093 }, | ||||
| 		{ "papaya whip", 0xffefd5 }, | ||||
| 		{ "peach puff", 0xffdab9 }, | ||||
| 		{ "peru", 0xcd853f }, | ||||
| 		{ "pink", 0xffc0cb }, | ||||
| 		{ "pink1", 0xffb5c5 }, | ||||
| 		{ "pink2", 0xeea9b8 }, | ||||
| 		{ "pink3", 0xcd919e }, | ||||
| 		{ "pink4", 0x8b636c }, | ||||
| 		{ "plum", 0xdda0dd }, | ||||
| 		{ "plum1", 0xffbbff }, | ||||
| 		{ "plum2", 0xeeaeee }, | ||||
| 		{ "plum3", 0xcd96cd }, | ||||
| 		{ "plum4", 0x8b668b }, | ||||
| 		{ "powder blue", 0xb0e0e6 }, | ||||
| 		{ "purple", 0xa020f0 }, | ||||
| 		{ "purple1", 0x9b30ff }, | ||||
| 		{ "purple2", 0x912cee }, | ||||
| 		{ "purple3", 0x7d26cd }, | ||||
| 		{ "purple4", 0x551a8b }, | ||||
| 		{ "rebecca purple", 0x663399 }, | ||||
| 		{ "red", 0xff0000 }, | ||||
| 		{ "red1", 0xff0000 }, | ||||
| 		{ "red2", 0xee0000 }, | ||||
| 		{ "red3", 0xcd0000 }, | ||||
| 		{ "red4", 0x8b0000 }, | ||||
| 		{ "rosy brown", 0xbc8f8f }, | ||||
| 		{ "royal blue", 0x4169e1 }, | ||||
| 		{ "saddle brown", 0x8b4513 }, | ||||
| 		{ "salmon", 0xfa8072 }, | ||||
| 		{ "salmon1", 0xff8c69 }, | ||||
| 		{ "salmon2", 0xee8262 }, | ||||
| 		{ "salmon3", 0xcd7054 }, | ||||
| 		{ "salmon4", 0x8b4c39 }, | ||||
| 		{ "sandy brown", 0xf4a460 }, | ||||
| 		{ "sea green", 0x2e8b57 }, | ||||
| 		{ "seashell", 0xfff5ee }, | ||||
| 		{ "seashell1", 0xfff5ee }, | ||||
| 		{ "seashell2", 0xeee5de }, | ||||
| 		{ "seashell3", 0xcdc5bf }, | ||||
| 		{ "seashell4", 0x8b8682 }, | ||||
| 		{ "sienna", 0xa0522d }, | ||||
| 		{ "sienna1", 0xff8247 }, | ||||
| 		{ "sienna2", 0xee7942 }, | ||||
| 		{ "sienna3", 0xcd6839 }, | ||||
| 		{ "sienna4", 0x8b4726 }, | ||||
| 		{ "silver", 0xc0c0c0 }, | ||||
| 		{ "sky blue", 0x87ceeb }, | ||||
| 		{ "slate blue", 0x6a5acd }, | ||||
| 		{ "slate gray", 0x708090 }, | ||||
| 		{ "slate grey", 0x708090 }, | ||||
| 		{ "snow", 0xfffafa }, | ||||
| 		{ "snow1", 0xfffafa }, | ||||
| 		{ "snow2", 0xeee9e9 }, | ||||
| 		{ "snow3", 0xcdc9c9 }, | ||||
| 		{ "snow4", 0x8b8989 }, | ||||
| 		{ "spring green", 0x00ff7f }, | ||||
| 		{ "steel blue", 0x4682b4 }, | ||||
| 		{ "tan", 0xd2b48c }, | ||||
| 		{ "tan1", 0xffa54f }, | ||||
| 		{ "tan2", 0xee9a49 }, | ||||
| 		{ "tan3", 0xcd853f }, | ||||
| 		{ "tan4", 0x8b5a2b }, | ||||
| 		{ "teal", 0x008080 }, | ||||
| 		{ "thistle", 0xd8bfd8 }, | ||||
| 		{ "thistle1", 0xffe1ff }, | ||||
| 		{ "thistle2", 0xeed2ee }, | ||||
| 		{ "thistle3", 0xcdb5cd }, | ||||
| 		{ "thistle4", 0x8b7b8b }, | ||||
| 		{ "tomato", 0xff6347 }, | ||||
| 		{ "tomato1", 0xff6347 }, | ||||
| 		{ "tomato2", 0xee5c42 }, | ||||
| 		{ "tomato3", 0xcd4f39 }, | ||||
| 		{ "tomato4", 0x8b3626 }, | ||||
| 		{ "turquoise", 0x40e0d0 }, | ||||
| 		{ "turquoise1", 0x00f5ff }, | ||||
| 		{ "turquoise2", 0x00e5ee }, | ||||
| 		{ "turquoise3", 0x00c5cd }, | ||||
| 		{ "turquoise4", 0x00868b }, | ||||
| 		{ "violet red", 0xd02090 }, | ||||
| 		{ "violet", 0xee82ee }, | ||||
| 		{ "web gray", 0x808080 }, | ||||
| 		{ "web green", 0x008000 }, | ||||
| 		{ "web grey", 0x808080 }, | ||||
| 		{ "web maroon", 0x800000 }, | ||||
| 		{ "web purple", 0x800080 }, | ||||
| 		{ "wheat", 0xf5deb3 }, | ||||
| 		{ "wheat1", 0xffe7ba }, | ||||
| 		{ "wheat2", 0xeed8ae }, | ||||
| 		{ "wheat3", 0xcdba96 }, | ||||
| 		{ "wheat4", 0x8b7e66 }, | ||||
| 		{ "white smoke", 0xf5f5f5 }, | ||||
| 		{ "white", 0xffffff }, | ||||
| 		{ "x11 gray", 0xbebebe }, | ||||
| 		{ "x11 green", 0x00ff00 }, | ||||
| 		{ "x11 grey", 0xbebebe }, | ||||
| 		{ "x11 maroon", 0xb03060 }, | ||||
| 		{ "x11 purple", 0xa020f0 }, | ||||
| 		{ "yellow green", 0x9acd32 }, | ||||
| 		{ "yellow", 0xffff00 }, | ||||
| 		{ "yellow1", 0xffff00 }, | ||||
| 		{ "yellow2", 0xeeee00 }, | ||||
| 		{ "yellow3", 0xcdcd00 }, | ||||
| 		{ "yellow4", 0x8b8b00 } | ||||
| 	}; | ||||
| 	u_int	i; | ||||
| 	int	c; | ||||
|  | ||||
| 	if (strncmp(name, "grey", 4) == 0 || strncmp(name, "gray", 4) == 0) { | ||||
| 		if (!isdigit((u_char)name[4])) | ||||
| 			return (0xbebebe|COLOUR_FLAG_RGB); | ||||
| 		c = round(2.55 * atoi(name + 4)); | ||||
| 		if (c < 0 || c > 255) | ||||
| 			return (-1); | ||||
| 		return (colour_join_rgb(c, c, c)); | ||||
| 	} | ||||
| 	for (i = 0; i < nitems(colours); i++) { | ||||
| 		if (strcasecmp(colours[i].name, name) == 0) | ||||
| 			return (colours[i].c|COLOUR_FLAG_RGB); | ||||
| 	} | ||||
| 	return (-1); | ||||
| } | ||||
|   | ||||
							
								
								
									
										37
									
								
								compat.h
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								compat.h
									
									
									
									
									
								
							| @@ -27,6 +27,19 @@ | ||||
| #include <termios.h> | ||||
| #include <wchar.h> | ||||
|  | ||||
| #ifdef HAVE_EVENT2_EVENT_H | ||||
| #include <event2/event.h> | ||||
| #include <event2/event_compat.h> | ||||
| #include <event2/event_struct.h> | ||||
| #include <event2/buffer.h> | ||||
| #include <event2/buffer_compat.h> | ||||
| #include <event2/bufferevent.h> | ||||
| #include <event2/bufferevent_struct.h> | ||||
| #include <event2/bufferevent_compat.h> | ||||
| #else | ||||
| #include <event.h> | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_MALLOC_TRIM | ||||
| #include <malloc.h> | ||||
| #endif | ||||
| @@ -52,6 +65,9 @@ | ||||
| #ifndef __packed | ||||
| #define __packed __attribute__ ((__packed__)) | ||||
| #endif | ||||
| #ifndef __weak | ||||
| #define __weak __attribute__ ((__weak__)) | ||||
| #endif | ||||
|  | ||||
| #ifndef ECHOPRT | ||||
| #define ECHOPRT 0 | ||||
| @@ -110,6 +126,10 @@ void	warnx(const char *, ...); | ||||
| #define pledge(s, p) (0) | ||||
| #endif | ||||
|  | ||||
| #ifndef IMAXBEL | ||||
| #define IMAXBEL 0 | ||||
| #endif | ||||
|  | ||||
| #ifdef HAVE_STDINT_H | ||||
| #include <stdint.h> | ||||
| #else | ||||
| @@ -245,6 +265,13 @@ void	warnx(const char *, ...); | ||||
| #define HOST_NAME_MAX 255 | ||||
| #endif | ||||
|  | ||||
| #ifndef CLOCK_REALTIME | ||||
| #define CLOCK_REALTIME 0 | ||||
| #endif | ||||
| #ifndef CLOCK_MONOTONIC | ||||
| #define CLOCK_MONOTONIC CLOCK_REALTIME | ||||
| #endif | ||||
|  | ||||
| #ifndef HAVE_FLOCK | ||||
| #define LOCK_SH 0 | ||||
| #define LOCK_EX 0 | ||||
| @@ -322,6 +349,11 @@ const char	*getprogname(void); | ||||
| void		 setproctitle(const char *, ...); | ||||
| #endif | ||||
|  | ||||
| #ifndef HAVE_CLOCK_GETTIME | ||||
| /* clock_gettime.c */ | ||||
| int		 clock_gettime(int, struct timespec *); | ||||
| #endif | ||||
|  | ||||
| #ifndef HAVE_B64_NTOP | ||||
| /* base64.c */ | ||||
| #undef b64_ntop | ||||
| @@ -391,6 +423,11 @@ int		 utf8proc_mbtowc(wchar_t *, const char *, size_t); | ||||
| int		 utf8proc_wctomb(char *, wchar_t); | ||||
| #endif | ||||
|  | ||||
| #ifdef NEED_FUZZING | ||||
| /* tmux.c */ | ||||
| #define main __weak main | ||||
| #endif | ||||
|  | ||||
| /* getopt.c */ | ||||
| extern int	BSDopterr; | ||||
| extern int	BSDoptind; | ||||
|   | ||||
							
								
								
									
										37
									
								
								compat/clock_gettime.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								compat/clock_gettime.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| /* | ||||
|  * Copyright (c) 2021 Nicholas Marriott <nicholas.marriott@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | ||||
|  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | ||||
|  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/time.h> | ||||
|  | ||||
| #include "compat.h" | ||||
|  | ||||
| #ifndef TIMEVAL_TO_TIMESPEC | ||||
| #define TIMEVAL_TO_TIMESPEC(tv, ts) do {	\ | ||||
| 	(ts)->tv_sec = (tv)->tv_sec;		\ | ||||
| 	(ts)->tv_nsec = (tv)->tv_usec * 1000;	\ | ||||
| } while (0) | ||||
| #endif | ||||
|  | ||||
| int | ||||
| clock_gettime(int clock, struct timespec *ts) | ||||
| { | ||||
| 	struct timeval	tv; | ||||
|  | ||||
| 	gettimeofday(&tv, NULL); | ||||
| 	TIMEVAL_TO_TIMESPEC(&tv, ts); | ||||
| 	return 0; | ||||
| } | ||||
| @@ -50,6 +50,8 @@ | ||||
| # include <libproc.h> | ||||
| #endif | ||||
|  | ||||
| #include "compat.h" | ||||
|  | ||||
| #ifndef OPEN_MAX | ||||
| # define OPEN_MAX	256 | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										82
									
								
								compat/forkpty-haiku.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								compat/forkpty-haiku.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| /* | ||||
|  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | ||||
|  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | ||||
|  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/ioctl.h> | ||||
|  | ||||
| #include <fcntl.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "compat.h" | ||||
|  | ||||
| void fatal(const char *, ...); | ||||
| void fatalx(const char *, ...); | ||||
|  | ||||
| pid_t | ||||
| forkpty(int *master, char *name, struct termios *tio, struct winsize *ws) | ||||
| { | ||||
| 	int	slave = -1; | ||||
| 	char   *path; | ||||
| 	pid_t	pid; | ||||
|  | ||||
| 	if ((*master = open("/dev/ptmx", O_RDWR|O_NOCTTY)) == -1) | ||||
| 		return (-1); | ||||
| 	if (grantpt(*master) != 0) | ||||
| 		goto out; | ||||
| 	if (unlockpt(*master) != 0) | ||||
| 		goto out; | ||||
|  | ||||
| 	if ((path = ptsname(*master)) == NULL) | ||||
| 		goto out; | ||||
| 	if (name != NULL) | ||||
| 		strlcpy(name, path, TTY_NAME_MAX); | ||||
| 	if ((slave = open(path, O_RDWR|O_NOCTTY)) == -1) | ||||
| 		goto out; | ||||
|  | ||||
| 	switch (pid = fork()) { | ||||
| 	case -1: | ||||
| 		goto out; | ||||
| 	case 0: | ||||
| 		close(*master); | ||||
|  | ||||
| 		setsid(); | ||||
| 		if (ioctl(slave, TIOCSCTTY, NULL) == -1) | ||||
| 			fatal("ioctl failed"); | ||||
|  | ||||
| 		if (tio != NULL && tcsetattr(slave, TCSAFLUSH, tio) == -1) | ||||
| 			fatal("tcsetattr failed"); | ||||
| 		if (ioctl(slave, TIOCSWINSZ, ws) == -1) | ||||
| 			fatal("ioctl failed"); | ||||
|  | ||||
| 		dup2(slave, 0); | ||||
| 		dup2(slave, 1); | ||||
| 		dup2(slave, 2); | ||||
| 		if (slave > 2) | ||||
| 			close(slave); | ||||
| 		return (0); | ||||
| 	} | ||||
|  | ||||
| 	close(slave); | ||||
| 	return (pid); | ||||
|  | ||||
| out: | ||||
| 	if (*master != -1) | ||||
| 		close(*master); | ||||
| 	if (slave != -1) | ||||
| 		close(slave); | ||||
| 	return (-1); | ||||
| } | ||||
							
								
								
									
										187
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										187
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| # configure.ac | ||||
|  | ||||
| AC_INIT([tmux], 3.2-rc3) | ||||
| AC_INIT([tmux], 3.2-rc4) | ||||
| AC_PREREQ([2.60]) | ||||
|  | ||||
| AC_CONFIG_AUX_DIR(etc) | ||||
| @@ -21,6 +21,26 @@ SAVED_CFLAGS="$CFLAGS" | ||||
| SAVED_CPPFLAGS="$CPPFLAGS" | ||||
| SAVED_LDFLAGS="$LDFLAGS" | ||||
|  | ||||
| # Is this oss-fuzz build? | ||||
| AC_ARG_ENABLE( | ||||
| 	fuzzing, | ||||
| 	AC_HELP_STRING(--enable-fuzzing, build fuzzers) | ||||
| ) | ||||
| AC_ARG_VAR( | ||||
| 	FUZZING_LIBS, | ||||
| 	AC_HELP_STRING(libraries to link fuzzing targets with) | ||||
| ) | ||||
|  | ||||
| # Set up convenient fuzzing defaults before initializing compiler. | ||||
| if test "x$enable_fuzzing" = xyes; then | ||||
| 	AC_DEFINE(NEED_FUZZING) | ||||
| 	test "x$CC" == x && CC=clang | ||||
| 	test "x$FUZZING_LIBS" == x && \ | ||||
| 		FUZZING_LIBS="-fsanitize=fuzzer" | ||||
| 	test "x$SAVED_CFLAGS" == x && \ | ||||
| 		AM_CFLAGS="-g -fsanitize=fuzzer-no-link,address" | ||||
| fi | ||||
|  | ||||
| # Set up the compiler in two different ways and say yes we may want to install. | ||||
| AC_PROG_CC | ||||
| AM_PROG_CC_C_O | ||||
| @@ -54,8 +74,11 @@ if test "x$enable_static" = xyes; then | ||||
| 	LDFLAGS="$AM_LDFLAGS $SAVED_LDFLAGS" | ||||
| fi | ||||
|  | ||||
| # Do we need fuzzers? | ||||
| AM_CONDITIONAL(NEED_FUZZING, test "x$enable_fuzzing" = xyes) | ||||
|  | ||||
| # Is this gcc? | ||||
| AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes) | ||||
| AM_CONDITIONAL(IS_GCC, test "x$GCC" = xyes -a "x$enable_fuzzing" != xyes) | ||||
|  | ||||
| # Is this Sun CC? | ||||
| AC_EGREP_CPP( | ||||
| @@ -110,6 +133,7 @@ AC_CHECK_FUNCS([ \ | ||||
| AC_REPLACE_FUNCS([ \ | ||||
| 	asprintf \ | ||||
| 	cfmakeraw \ | ||||
| 	clock_gettime \ | ||||
| 	closefrom \ | ||||
| 	explicit_bzero \ | ||||
| 	fgetln \ | ||||
| @@ -119,8 +143,6 @@ AC_REPLACE_FUNCS([ \ | ||||
| 	getline \ | ||||
| 	getprogname \ | ||||
| 	memmem \ | ||||
| 	recallocarray \ | ||||
| 	reallocarray \ | ||||
| 	setenv \ | ||||
| 	setproctitle \ | ||||
| 	strcasestr \ | ||||
| @@ -132,6 +154,26 @@ AC_REPLACE_FUNCS([ \ | ||||
| ]) | ||||
| AC_FUNC_STRNLEN | ||||
|  | ||||
| # Clang sanitizers wrap reallocarray even if it isn't available on the target | ||||
| # system. When compiled it always returns NULL and crashes the program. To | ||||
| # detect this we need a more complicated test. | ||||
| AC_MSG_CHECKING([for working reallocarray]) | ||||
| AC_RUN_IFELSE([AC_LANG_PROGRAM( | ||||
| 		[#include <stdlib.h>], | ||||
| 		[return (reallocarray(NULL, 1, 1) == NULL);] | ||||
| 	)], | ||||
| 	AC_MSG_RESULT(yes), | ||||
| 	[AC_LIBOBJ(reallocarray) AC_MSG_RESULT([no])] | ||||
| ) | ||||
| AC_MSG_CHECKING([for working recallocarray]) | ||||
| AC_RUN_IFELSE([AC_LANG_PROGRAM( | ||||
| 		[#include <stdlib.h>], | ||||
| 		[return (recallocarray(NULL, 1, 1, 1) == NULL);] | ||||
| 	)], | ||||
| 	AC_MSG_RESULT(yes), | ||||
| 	[AC_LIBOBJ(recallocarray) AC_MSG_RESULT([no])] | ||||
| ) | ||||
|  | ||||
| # Look for clock_gettime. Must come before event_init. | ||||
| AC_SEARCH_LIBS(clock_gettime, rt) | ||||
|  | ||||
| @@ -141,88 +183,112 @@ AC_SEARCH_LIBS(clock_gettime, rt) | ||||
| # implementations. | ||||
| AC_LIBOBJ(getopt) | ||||
|  | ||||
| # Look for libevent. | ||||
| # Look for libevent. Try libevent_core or libevent with pkg-config first then | ||||
| # look for the library. | ||||
| PKG_CHECK_MODULES( | ||||
| 	LIBEVENT, | ||||
| 	libevent, | ||||
| 	LIBEVENT_CORE, | ||||
| 	[libevent_core >= 2], | ||||
| 	[ | ||||
| 		AM_CFLAGS="$LIBEVENT_CFLAGS $AM_CFLAGS" | ||||
| 		CFLAGS="$AM_CFLAGS $SAVED_CFLAGS" | ||||
| 		LIBS="$LIBEVENT_LIBS $LIBS" | ||||
| 		AM_CPPFLAGS="$LIBEVENT_CORE_CFLAGS $AM_CPPFLAGS" | ||||
| 		CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" | ||||
| 		LIBS="$LIBEVENT_CORE_LIBS $LIBS" | ||||
| 		found_libevent=yes | ||||
| 	], | ||||
| 	found_libevent=no | ||||
| ) | ||||
| if test x$found_libevent = xno; then | ||||
| 	PKG_CHECK_MODULES( | ||||
| 		LIBEVENT, | ||||
| 		[libevent >= 2], | ||||
| 		[ | ||||
| 			AM_CPPFLAGS="$LIBEVENT_CFLAGS $AM_CPPFLAGS" | ||||
| 			CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS" | ||||
| 			LIBS="$LIBEVENT_LIBS $LIBS" | ||||
| 			found_libevent=yes | ||||
| 		], | ||||
| 		found_libevent=no | ||||
| 	) | ||||
| fi | ||||
| if test x$found_libevent = xno; then | ||||
| 	AC_SEARCH_LIBS( | ||||
| 		event_init, | ||||
| 		[event_core event event-1.4], | ||||
| 		found_libevent=yes, | ||||
| 		found_libevent=no | ||||
| 	) | ||||
| fi | ||||
| AC_CHECK_HEADER( | ||||
| 	event2/event.h, | ||||
| 	AC_DEFINE(HAVE_EVENT2_EVENT_H), | ||||
| 	[ | ||||
| 		AC_SEARCH_LIBS( | ||||
| 			event_init, | ||||
| 			[event event-1.4 event2], | ||||
| 			found_libevent=yes, | ||||
| 		AC_CHECK_HEADER( | ||||
| 			event.h, | ||||
| 			AC_DEFINE(HAVE_EVENT_H), | ||||
| 			found_libevent=no | ||||
| 		) | ||||
| 	] | ||||
| ) | ||||
| AC_CHECK_HEADER( | ||||
| 	event.h, | ||||
| 	, | ||||
| 	found_libevent=no | ||||
| ) | ||||
| if test "x$found_libevent" = xno; then | ||||
| 	AC_MSG_ERROR("libevent not found") | ||||
| fi | ||||
|  | ||||
| # Look for ncurses. | ||||
| # Look for ncurses or curses. Try pkg-config first then directly for the | ||||
| # library. | ||||
| PKG_CHECK_MODULES( | ||||
| 	LIBTINFO, | ||||
| 	tinfo, | ||||
| 	found_ncurses=yes, | ||||
| 	[ | ||||
| 		AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS" | ||||
| 		CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS" | ||||
| 		LIBS="$LIBTINFO_LIBS $LIBS" | ||||
| 		found_ncurses=yes | ||||
| 	], | ||||
| 	found_ncurses=no | ||||
| ) | ||||
| if test "x$found_ncurses" = xno; then | ||||
| 	PKG_CHECK_MODULES( | ||||
| 		LIBNCURSES, | ||||
| 		ncurses, | ||||
| 		found_ncurses=yes, | ||||
| 		[ | ||||
| 			AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS" | ||||
| 			CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS" | ||||
| 			LIBS="$LIBNCURSES_LIBS $LIBS" | ||||
| 			found_ncurses=yes | ||||
| 		], | ||||
| 		found_ncurses=no | ||||
| 	) | ||||
| fi | ||||
| if test "x$found_ncurses" = xno; then | ||||
| 	PKG_CHECK_MODULES( | ||||
| 		LIBNCURSES, | ||||
| 		LIBNCURSESW, | ||||
| 		ncursesw, | ||||
| 		found_ncurses=yes, | ||||
| 		[ | ||||
| 			AM_CPPFLAGS="$LIBNCURSESW_CFLAGS $AM_CPPFLAGS" | ||||
| 			CPPFLAGS="$LIBNCURSESW_CFLAGS $SAVED_CPPFLAGS" | ||||
| 			LIBS="$LIBNCURSESW_LIBS $LIBS" | ||||
| 			found_ncurses=yes | ||||
| 		], | ||||
| 		found_ncurses=no | ||||
| 	) | ||||
| fi | ||||
| if test "x$found_ncurses" = xyes; then | ||||
| 	AM_CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $AM_CFLAGS" | ||||
| 	CFLAGS="$LIBNCURSES_CFLAGS $LIBTINFO_CFLAGS $CFLAGS" | ||||
| 	LIBS="$LIBNCURSES_LIBS $LIBTINFO_LIBS $LIBS" | ||||
| else | ||||
| 	# pkg-config didn't work, try ncurses. | ||||
| 	AC_CHECK_LIB( | ||||
| 		tinfo, | ||||
| if test "x$found_ncurses" = xno; then | ||||
| 	AC_SEARCH_LIBS( | ||||
| 		setupterm, | ||||
| 		[tinfo ncurses ncursesw], | ||||
| 		found_ncurses=yes, | ||||
| 		found_ncurses=no | ||||
| 	) | ||||
| 	if test "x$found_ncurses" = xno; then | ||||
| 		AC_CHECK_LIB( | ||||
| 			ncurses, | ||||
| 			setupterm, | ||||
| 			found_ncurses=yes, | ||||
| 			found_ncurses=no | ||||
| 		) | ||||
| 	fi | ||||
| 	if test "x$found_ncurses" = xyes; then | ||||
| 		AC_CHECK_HEADER( | ||||
| 			ncurses.h, | ||||
| 			LIBS="$LIBS -lncurses", | ||||
| 			found_ncurses=no) | ||||
| 			found_ncurses=no | ||||
| 		) | ||||
| 	fi | ||||
| fi | ||||
| if test "x$found_ncurses" = xyes; then | ||||
| 	AC_DEFINE(HAVE_NCURSES_H) | ||||
| else | ||||
| 	# No ncurses, try curses. | ||||
| 	AC_CHECK_LIB( | ||||
| 		curses, | ||||
| 		setupterm, | ||||
| @@ -232,7 +298,8 @@ else | ||||
| 	AC_CHECK_HEADER( | ||||
| 		curses.h, | ||||
| 		, | ||||
| 		found_curses=no) | ||||
| 		found_curses=no | ||||
| 	) | ||||
| 	if test "x$found_curses" = xyes; then | ||||
| 		LIBS="$LIBS -lcurses" | ||||
| 		AC_DEFINE(HAVE_CURSES_H) | ||||
| @@ -298,12 +365,11 @@ AC_TRY_LINK( | ||||
| 	found_b64_ntop=yes, | ||||
| 	found_b64_ntop=no | ||||
| ) | ||||
| AC_MSG_RESULT($found_b64_ntop) | ||||
| OLD_LIBS="$LIBS" | ||||
| if test "x$found_b64_ntop" = xno; then | ||||
| 	AC_MSG_RESULT(no) | ||||
|  | ||||
| 	AC_MSG_CHECKING(for b64_ntop with -lresolv) | ||||
| 	OLD_LIBS="$LIBS" | ||||
| 	LIBS="$LIBS -lresolv" | ||||
| 	LIBS="$OLD_LIBS -lresolv" | ||||
| 	AC_TRY_LINK( | ||||
| 		[ | ||||
| 			#include <sys/types.h> | ||||
| @@ -314,15 +380,27 @@ if test "x$found_b64_ntop" = xno; then | ||||
| 		found_b64_ntop=yes, | ||||
| 		found_b64_ntop=no | ||||
| 	) | ||||
| 	if test "x$found_b64_ntop" = xno; then | ||||
| 		LIBS="$OLD_LIBS" | ||||
| 		AC_MSG_RESULT(no) | ||||
| 	fi | ||||
| 	AC_MSG_RESULT($found_b64_ntop) | ||||
| fi | ||||
| if test "x$found_b64_ntop" = xno; then | ||||
| 	AC_MSG_CHECKING(for b64_ntop with -lnetwork) | ||||
| 	LIBS="$OLD_LIBS -lnetwork" | ||||
| 	AC_TRY_LINK( | ||||
| 		[ | ||||
| 			#include <sys/types.h> | ||||
| 			#include <netinet/in.h> | ||||
| 			#include <resolv.h> | ||||
| 		], | ||||
| 		[b64_ntop(NULL, 0, NULL, 0);], | ||||
| 		found_b64_ntop=yes, | ||||
| 		found_b64_ntop=no | ||||
| 	) | ||||
| 	AC_MSG_RESULT($found_b64_ntop) | ||||
| fi | ||||
| if test "x$found_b64_ntop" = xyes; then | ||||
| 	AC_DEFINE(HAVE_B64_NTOP) | ||||
| 	AC_MSG_RESULT(yes) | ||||
| else | ||||
| 	LIBS="$OLD_LIBS" | ||||
| 	AC_LIBOBJ(base64) | ||||
| fi | ||||
|  | ||||
| @@ -658,6 +736,10 @@ case "$host_os" in | ||||
| 		AC_MSG_RESULT(cygwin) | ||||
| 		PLATFORM=cygwin | ||||
| 		;; | ||||
| 	*haiku*) | ||||
| 		AC_MSG_RESULT(haiku) | ||||
| 		PLATFORM=haiku | ||||
| 		;; | ||||
| 	*) | ||||
| 		AC_MSG_RESULT(unknown) | ||||
| 		PLATFORM=unknown | ||||
| @@ -673,6 +755,7 @@ AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd) | ||||
| AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd) | ||||
| AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos) | ||||
| AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux) | ||||
| AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku) | ||||
| AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown) | ||||
|  | ||||
| # Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user | ||||
|   | ||||
| @@ -49,7 +49,7 @@ control_notify_window_layout_changed(struct window *w) | ||||
| 	char		*cp; | ||||
|  | ||||
| 	template = "%layout-change #{window_id} #{window_layout} " | ||||
| 	    "#{window_visible_layout} #{window_flags}"; | ||||
| 	    "#{window_visible_layout} #{window_raw_flags}"; | ||||
|  | ||||
| 	TAILQ_FOREACH(c, &clients, entry) { | ||||
| 		if (!CONTROL_SHOULD_NOTIFY_CLIENT(c) || c->session == NULL) | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <event.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <time.h> | ||||
|   | ||||
							
								
								
									
										454
									
								
								file.c
									
									
									
									
									
								
							
							
						
						
									
										454
									
								
								file.c
									
									
									
									
									
								
							| @@ -27,10 +27,17 @@ | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| /* | ||||
|  * IPC file handling. Both client and server use the same data structures | ||||
|  * (client_file and client_files) to store list of active files. Most functions | ||||
|  * are for use either in client or server but not both. | ||||
|  */ | ||||
|  | ||||
| static int	file_next_stream = 3; | ||||
|  | ||||
| RB_GENERATE(client_files, client_file, entry, file_cmp); | ||||
|  | ||||
| /* Get path for file, either as given or from working directory. */ | ||||
| static char * | ||||
| file_get_path(struct client *c, const char *file) | ||||
| { | ||||
| @@ -43,6 +50,7 @@ file_get_path(struct client *c, const char *file) | ||||
| 	return (path); | ||||
| } | ||||
|  | ||||
| /* Tree comparison function. */ | ||||
| int | ||||
| file_cmp(struct client_file *cf1, struct client_file *cf2) | ||||
| { | ||||
| @@ -53,11 +61,47 @@ file_cmp(struct client_file *cf1, struct client_file *cf2) | ||||
| 	return (0); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Create a file object in the client process - the peer is the server to send | ||||
|  * messages to. Check callback is fired when the file is finished with so the | ||||
|  * process can decide if it needs to exit (if it is waiting for files to | ||||
|  * flush). | ||||
|  */ | ||||
| struct client_file * | ||||
| file_create(struct client *c, int stream, client_file_cb cb, void *cbdata) | ||||
| file_create_with_peer(struct tmuxpeer *peer, struct client_files *files, | ||||
|     int stream, client_file_cb cb, void *cbdata) | ||||
| { | ||||
| 	struct client_file	*cf; | ||||
|  | ||||
| 	cf = xcalloc(1, sizeof *cf); | ||||
| 	cf->c = NULL; | ||||
| 	cf->references = 1; | ||||
| 	cf->stream = stream; | ||||
|  | ||||
| 	cf->buffer = evbuffer_new(); | ||||
| 	if (cf->buffer == NULL) | ||||
| 		fatalx("out of memory"); | ||||
|  | ||||
| 	cf->cb = cb; | ||||
| 	cf->data = cbdata; | ||||
|  | ||||
| 	cf->peer = peer; | ||||
| 	cf->tree = files; | ||||
| 	RB_INSERT(client_files, files, cf); | ||||
|  | ||||
| 	return (cf); | ||||
| } | ||||
|  | ||||
| /* Create a file object in the server, communicating with the given client. */ | ||||
| struct client_file * | ||||
| file_create_with_client(struct client *c, int stream, client_file_cb cb, | ||||
|     void *cbdata) | ||||
| { | ||||
| 	struct client_file	*cf; | ||||
|  | ||||
| 	if (c != NULL && (c->flags & CLIENT_ATTACHED)) | ||||
| 		c = NULL; | ||||
|  | ||||
| 	cf = xcalloc(1, sizeof *cf); | ||||
| 	cf->c = c; | ||||
| 	cf->references = 1; | ||||
| @@ -71,6 +115,8 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata) | ||||
| 	cf->data = cbdata; | ||||
|  | ||||
| 	if (cf->c != NULL) { | ||||
| 		cf->peer = cf->c->peer; | ||||
| 		cf->tree = &cf->c->files; | ||||
| 		RB_INSERT(client_files, &cf->c->files, cf); | ||||
| 		cf->c->references++; | ||||
| 	} | ||||
| @@ -78,6 +124,7 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata) | ||||
| 	return (cf); | ||||
| } | ||||
|  | ||||
| /* Free a file. */ | ||||
| void | ||||
| file_free(struct client_file *cf) | ||||
| { | ||||
| @@ -87,13 +134,15 @@ file_free(struct client_file *cf) | ||||
| 	evbuffer_free(cf->buffer); | ||||
| 	free(cf->path); | ||||
|  | ||||
| 	if (cf->c != NULL) { | ||||
| 		RB_REMOVE(client_files, &cf->c->files, cf); | ||||
| 	if (cf->tree != NULL) | ||||
| 		RB_REMOVE(client_files, cf->tree, cf); | ||||
| 	if (cf->c != NULL) | ||||
| 		server_client_unref(cf->c); | ||||
| 	} | ||||
|  | ||||
| 	free(cf); | ||||
| } | ||||
|  | ||||
| /* Event to fire the done callback. */ | ||||
| static void | ||||
| file_fire_done_cb(__unused int fd, __unused short events, void *arg) | ||||
| { | ||||
| @@ -105,21 +154,22 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg) | ||||
| 	file_free(cf); | ||||
| } | ||||
|  | ||||
| /* Add an event to fire the done callback (used by the server). */ | ||||
| void | ||||
| file_fire_done(struct client_file *cf) | ||||
| { | ||||
| 	event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL); | ||||
| } | ||||
|  | ||||
| /* Fire the read callback. */ | ||||
| void | ||||
| file_fire_read(struct client_file *cf) | ||||
| { | ||||
| 	struct client	*c = cf->c; | ||||
|  | ||||
| 	if (cf->cb != NULL) | ||||
| 		cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data); | ||||
| 		cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data); | ||||
| } | ||||
|  | ||||
| /* Can this file be printed to? */ | ||||
| int | ||||
| file_can_print(struct client *c) | ||||
| { | ||||
| @@ -130,6 +180,7 @@ file_can_print(struct client *c) | ||||
| 	return (1); | ||||
| } | ||||
|  | ||||
| /* Print a message to a file. */ | ||||
| void | ||||
| file_print(struct client *c, const char *fmt, ...) | ||||
| { | ||||
| @@ -140,6 +191,7 @@ file_print(struct client *c, const char *fmt, ...) | ||||
| 	va_end(ap); | ||||
| } | ||||
|  | ||||
| /* Print a message to a file. */ | ||||
| void | ||||
| file_vprint(struct client *c, const char *fmt, va_list ap) | ||||
| { | ||||
| @@ -151,7 +203,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap) | ||||
|  | ||||
| 	find.stream = 1; | ||||
| 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { | ||||
| 		cf = file_create(c, 1, NULL, NULL); | ||||
| 		cf = file_create_with_client(c, 1, NULL, NULL); | ||||
| 		cf->path = xstrdup("-"); | ||||
|  | ||||
| 		evbuffer_add_vprintf(cf->buffer, fmt, ap); | ||||
| @@ -166,6 +218,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Print a buffer to a file. */ | ||||
| void | ||||
| file_print_buffer(struct client *c, void *data, size_t size) | ||||
| { | ||||
| @@ -177,7 +230,7 @@ file_print_buffer(struct client *c, void *data, size_t size) | ||||
|  | ||||
| 	find.stream = 1; | ||||
| 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { | ||||
| 		cf = file_create(c, 1, NULL, NULL); | ||||
| 		cf = file_create_with_client(c, 1, NULL, NULL); | ||||
| 		cf->path = xstrdup("-"); | ||||
|  | ||||
| 		evbuffer_add(cf->buffer, data, size); | ||||
| @@ -192,6 +245,7 @@ file_print_buffer(struct client *c, void *data, size_t size) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Report an error to a file. */ | ||||
| void | ||||
| file_error(struct client *c, const char *fmt, ...) | ||||
| { | ||||
| @@ -206,7 +260,7 @@ file_error(struct client *c, const char *fmt, ...) | ||||
|  | ||||
| 	find.stream = 2; | ||||
| 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { | ||||
| 		cf = file_create(c, 2, NULL, NULL); | ||||
| 		cf = file_create_with_client(c, 2, NULL, NULL); | ||||
| 		cf->path = xstrdup("-"); | ||||
|  | ||||
| 		evbuffer_add_vprintf(cf->buffer, fmt, ap); | ||||
| @@ -223,19 +277,21 @@ file_error(struct client *c, const char *fmt, ...) | ||||
| 	va_end(ap); | ||||
| } | ||||
|  | ||||
| /* Write data to a file. */ | ||||
| void | ||||
| file_write(struct client *c, const char *path, int flags, const void *bdata, | ||||
|     size_t bsize, client_file_cb cb, void *cbdata) | ||||
| { | ||||
| 	struct client_file	*cf; | ||||
| 	FILE			*f; | ||||
| 	struct msg_write_open	*msg; | ||||
| 	size_t			 msglen; | ||||
| 	int			 fd = -1; | ||||
| 	u_int			 stream = file_next_stream++; | ||||
| 	FILE			*f; | ||||
| 	const char		*mode; | ||||
|  | ||||
| 	if (strcmp(path, "-") == 0) { | ||||
| 		cf = file_create(c, file_next_stream++, cb, cbdata); | ||||
| 		cf = file_create_with_client(c, stream, cb, cbdata); | ||||
| 		cf->path = xstrdup("-"); | ||||
|  | ||||
| 		fd = STDOUT_FILENO; | ||||
| @@ -248,7 +304,7 @@ file_write(struct client *c, const char *path, int flags, const void *bdata, | ||||
| 		goto skip; | ||||
| 	} | ||||
|  | ||||
| 	cf = file_create(c, file_next_stream++, cb, cbdata); | ||||
| 	cf = file_create_with_client(c, stream, cb, cbdata); | ||||
| 	cf->path = file_get_path(c, path); | ||||
|  | ||||
| 	if (c == NULL || c->flags & CLIENT_ATTACHED) { | ||||
| @@ -283,7 +339,7 @@ skip: | ||||
| 	msg->fd = fd; | ||||
| 	msg->flags = flags; | ||||
| 	memcpy(msg + 1, cf->path, msglen - sizeof *msg); | ||||
| 	if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) { | ||||
| 	if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) { | ||||
| 		free(msg); | ||||
| 		cf->error = EINVAL; | ||||
| 		goto done; | ||||
| @@ -295,18 +351,21 @@ done: | ||||
| 	file_fire_done(cf); | ||||
| } | ||||
|  | ||||
| /* Read a file. */ | ||||
| void | ||||
| file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) | ||||
| { | ||||
| 	struct client_file	*cf; | ||||
| 	FILE			*f; | ||||
| 	struct msg_read_open	*msg; | ||||
| 	size_t			 msglen, size; | ||||
| 	size_t			 msglen; | ||||
| 	int			 fd = -1; | ||||
| 	u_int			 stream = file_next_stream++; | ||||
| 	FILE			*f; | ||||
| 	size_t			 size; | ||||
| 	char			 buffer[BUFSIZ]; | ||||
|  | ||||
| 	if (strcmp(path, "-") == 0) { | ||||
| 		cf = file_create(c, file_next_stream++, cb, cbdata); | ||||
| 		cf = file_create_with_client(c, stream, cb, cbdata); | ||||
| 		cf->path = xstrdup("-"); | ||||
|  | ||||
| 		fd = STDIN_FILENO; | ||||
| @@ -319,7 +378,7 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) | ||||
| 		goto skip; | ||||
| 	} | ||||
|  | ||||
| 	cf = file_create(c, file_next_stream++, cb, cbdata); | ||||
| 	cf = file_create_with_client(c, stream, cb, cbdata); | ||||
| 	cf->path = file_get_path(c, path); | ||||
|  | ||||
| 	if (c == NULL || c->flags & CLIENT_ATTACHED) { | ||||
| @@ -355,7 +414,7 @@ skip: | ||||
| 	msg->stream = cf->stream; | ||||
| 	msg->fd = fd; | ||||
| 	memcpy(msg + 1, cf->path, msglen - sizeof *msg); | ||||
| 	if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) { | ||||
| 	if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) { | ||||
| 		free(msg); | ||||
| 		cf->error = EINVAL; | ||||
| 		goto done; | ||||
| @@ -367,21 +426,21 @@ done: | ||||
| 	file_fire_done(cf); | ||||
| } | ||||
|  | ||||
| /* Push event, fired if there is more writing to be done. */ | ||||
| static void | ||||
| file_push_cb(__unused int fd, __unused short events, void *arg) | ||||
| { | ||||
| 	struct client_file	*cf = arg; | ||||
| 	struct client		*c = cf->c; | ||||
|  | ||||
| 	if (~c->flags & CLIENT_DEAD) | ||||
| 	if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD) | ||||
| 		file_push(cf); | ||||
| 	file_free(cf); | ||||
| } | ||||
|  | ||||
| /* Push uwritten data to the client for a file, if it will accept it. */ | ||||
| void | ||||
| file_push(struct client_file *cf) | ||||
| { | ||||
| 	struct client		*c = cf->c; | ||||
| 	struct msg_write_data	*msg; | ||||
| 	size_t			 msglen, sent, left; | ||||
| 	struct msg_write_close	 close; | ||||
| @@ -397,21 +456,364 @@ file_push(struct client_file *cf) | ||||
| 		msg = xrealloc(msg, msglen); | ||||
| 		msg->stream = cf->stream; | ||||
| 		memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent); | ||||
| 		if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0) | ||||
| 		if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0) | ||||
| 			break; | ||||
| 		evbuffer_drain(cf->buffer, sent); | ||||
|  | ||||
| 		left = EVBUFFER_LENGTH(cf->buffer); | ||||
| 		log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream, | ||||
| 		    sent, left); | ||||
| 		log_debug("file %d sent %zu, left %zu", cf->stream, sent, left); | ||||
| 	} | ||||
| 	if (left != 0) { | ||||
| 		cf->references++; | ||||
| 		event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL); | ||||
| 	} else if (cf->stream > 2) { | ||||
| 		close.stream = cf->stream; | ||||
| 		proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close); | ||||
| 		proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close); | ||||
| 		file_fire_done(cf); | ||||
| 	} | ||||
| 	free(msg); | ||||
| } | ||||
|  | ||||
| /* Check if any files have data left to write. */ | ||||
| int | ||||
| file_write_left(struct client_files *files) | ||||
| { | ||||
| 	struct client_file	*cf; | ||||
| 	size_t			 left; | ||||
| 	int			 waiting = 0; | ||||
|  | ||||
| 	RB_FOREACH(cf, client_files, files) { | ||||
| 		if (cf->event == NULL) | ||||
| 			continue; | ||||
| 		left = EVBUFFER_LENGTH(cf->event->output); | ||||
| 		if (left != 0) { | ||||
| 			waiting++; | ||||
| 			log_debug("file %u %zu bytes left", cf->stream, left); | ||||
| 		} | ||||
| 	} | ||||
| 	return (waiting != 0); | ||||
| } | ||||
|  | ||||
| /* Client file write error callback. */ | ||||
| static void | ||||
| file_write_error_callback(__unused struct bufferevent *bev, __unused short what, | ||||
|     void *arg) | ||||
| { | ||||
| 	struct client_file	*cf = arg; | ||||
|  | ||||
| 	log_debug("write error file %d", cf->stream); | ||||
|  | ||||
| 	if (cf->cb != NULL) | ||||
| 		cf->cb(NULL, NULL, 0, -1, NULL, cf->data); | ||||
|  | ||||
| 	bufferevent_free(cf->event); | ||||
| 	cf->event = NULL; | ||||
|  | ||||
| 	close(cf->fd); | ||||
| 	cf->fd = -1; | ||||
| } | ||||
|  | ||||
| /* Client file write callback. */ | ||||
| static void | ||||
| file_write_callback(__unused struct bufferevent *bev, void *arg) | ||||
| { | ||||
| 	struct client_file	*cf = arg; | ||||
|  | ||||
| 	log_debug("write check file %d", cf->stream); | ||||
|  | ||||
| 	if (cf->cb != NULL) | ||||
| 		cf->cb(NULL, NULL, 0, -1, NULL, cf->data); | ||||
|  | ||||
| 	if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { | ||||
| 		bufferevent_free(cf->event); | ||||
| 		close(cf->fd); | ||||
| 		RB_REMOVE(client_files, cf->tree, cf); | ||||
| 		file_free(cf); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Handle a file write open message (client). */ | ||||
| void | ||||
| file_write_open(struct client_files *files, struct tmuxpeer *peer, | ||||
|     struct imsg *imsg, int allow_streams, int close_received, | ||||
|     client_file_cb cb, void *cbdata) | ||||
| { | ||||
| 	struct msg_write_open	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	const char		*path; | ||||
| 	struct msg_write_ready	 reply; | ||||
| 	struct client_file	 find, *cf; | ||||
| 	const int		 flags = O_NONBLOCK|O_WRONLY|O_CREAT; | ||||
| 	int			 error = 0; | ||||
|  | ||||
| 	if (msglen < sizeof *msg) | ||||
| 		fatalx("bad MSG_WRITE_OPEN size"); | ||||
| 	if (msglen == sizeof *msg) | ||||
| 		path = "-"; | ||||
| 	else | ||||
| 		path = (const char *)(msg + 1); | ||||
| 	log_debug("open write file %d %s", msg->stream, path); | ||||
|  | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, files, &find)) != NULL) { | ||||
| 		error = EBADF; | ||||
| 		goto reply; | ||||
| 	} | ||||
| 	cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata); | ||||
| 	if (cf->closed) { | ||||
| 		error = EBADF; | ||||
| 		goto reply; | ||||
| 	} | ||||
|  | ||||
| 	cf->fd = -1; | ||||
| 	if (msg->fd == -1) | ||||
| 		cf->fd = open(path, msg->flags|flags, 0644); | ||||
| 	else if (allow_streams) { | ||||
| 		if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO) | ||||
| 			errno = EBADF; | ||||
| 		else { | ||||
| 			cf->fd = dup(msg->fd); | ||||
| 			if (close_received) | ||||
| 				close(msg->fd); /* can only be used once */ | ||||
| 		} | ||||
| 	} else | ||||
| 	      errno = EBADF; | ||||
| 	if (cf->fd == -1) { | ||||
| 		error = errno; | ||||
| 		goto reply; | ||||
| 	} | ||||
|  | ||||
| 	cf->event = bufferevent_new(cf->fd, NULL, file_write_callback, | ||||
| 	    file_write_error_callback, cf); | ||||
| 	bufferevent_enable(cf->event, EV_WRITE); | ||||
| 	goto reply; | ||||
|  | ||||
| reply: | ||||
| 	reply.stream = msg->stream; | ||||
| 	reply.error = error; | ||||
| 	proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply); | ||||
| } | ||||
|  | ||||
| /* Handle a file write data message (client). */ | ||||
| void | ||||
| file_write_data(struct client_files *files, struct imsg *imsg) | ||||
| { | ||||
| 	struct msg_write_data	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	struct client_file	 find, *cf; | ||||
| 	size_t			 size = msglen - sizeof *msg; | ||||
|  | ||||
| 	if (msglen < sizeof *msg) | ||||
| 		fatalx("bad MSG_WRITE size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, files, &find)) == NULL) | ||||
| 		fatalx("unknown stream number"); | ||||
| 	log_debug("write %zu to file %d", size, cf->stream); | ||||
|  | ||||
| 	if (cf->event != NULL) | ||||
| 		bufferevent_write(cf->event, msg + 1, size); | ||||
| } | ||||
|  | ||||
| /* Handle a file write close message (client). */ | ||||
| void | ||||
| file_write_close(struct client_files *files, struct imsg *imsg) | ||||
| { | ||||
| 	struct msg_write_close	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	struct client_file	 find, *cf; | ||||
|  | ||||
| 	if (msglen != sizeof *msg) | ||||
| 		fatalx("bad MSG_WRITE_CLOSE size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, files, &find)) == NULL) | ||||
| 		fatalx("unknown stream number"); | ||||
| 	log_debug("close file %d", cf->stream); | ||||
|  | ||||
| 	if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { | ||||
| 		if (cf->event != NULL) | ||||
| 			bufferevent_free(cf->event); | ||||
| 		if (cf->fd != -1) | ||||
| 			close(cf->fd); | ||||
| 		RB_REMOVE(client_files, files, cf); | ||||
| 		file_free(cf); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Client file read error callback. */ | ||||
| static void | ||||
| file_read_error_callback(__unused struct bufferevent *bev, __unused short what, | ||||
|     void *arg) | ||||
| { | ||||
| 	struct client_file	*cf = arg; | ||||
| 	struct msg_read_done	 msg; | ||||
|  | ||||
| 	log_debug("read error file %d", cf->stream); | ||||
|  | ||||
| 	msg.stream = cf->stream; | ||||
| 	msg.error = 0; | ||||
| 	proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg); | ||||
|  | ||||
| 	bufferevent_free(cf->event); | ||||
| 	close(cf->fd); | ||||
| 	RB_REMOVE(client_files, cf->tree, cf); | ||||
| 	file_free(cf); | ||||
| } | ||||
|  | ||||
| /* Client file read callback. */ | ||||
| static void | ||||
| file_read_callback(__unused struct bufferevent *bev, void *arg) | ||||
| { | ||||
| 	struct client_file	*cf = arg; | ||||
| 	void			*bdata; | ||||
| 	size_t			 bsize; | ||||
| 	struct msg_read_data	*msg; | ||||
| 	size_t			 msglen; | ||||
|  | ||||
| 	msg = xmalloc(sizeof *msg); | ||||
| 	for (;;) { | ||||
| 		bdata = EVBUFFER_DATA(cf->event->input); | ||||
| 		bsize = EVBUFFER_LENGTH(cf->event->input); | ||||
|  | ||||
| 		if (bsize == 0) | ||||
| 			break; | ||||
| 		if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) | ||||
| 			bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; | ||||
| 		log_debug("read %zu from file %d", bsize, cf->stream); | ||||
|  | ||||
| 		msglen = (sizeof *msg) + bsize; | ||||
| 		msg = xrealloc(msg, msglen); | ||||
| 		msg->stream = cf->stream; | ||||
| 		memcpy(msg + 1, bdata, bsize); | ||||
| 		proc_send(cf->peer, MSG_READ, -1, msg, msglen); | ||||
|  | ||||
| 		evbuffer_drain(cf->event->input, bsize); | ||||
| 	} | ||||
| 	free(msg); | ||||
| } | ||||
|  | ||||
| /* Handle a file read open message (client). */ | ||||
| void | ||||
| file_read_open(struct client_files *files, struct tmuxpeer *peer, | ||||
|     struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb, | ||||
|     void *cbdata) | ||||
| { | ||||
| 	struct msg_read_open	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	const char		*path; | ||||
| 	struct msg_read_done	 reply; | ||||
| 	struct client_file	 find, *cf; | ||||
| 	const int		 flags = O_NONBLOCK|O_RDONLY; | ||||
| 	int			 error; | ||||
|  | ||||
| 	if (msglen < sizeof *msg) | ||||
| 		fatalx("bad MSG_READ_OPEN size"); | ||||
| 	if (msglen == sizeof *msg) | ||||
| 		path = "-"; | ||||
| 	else | ||||
| 		path = (const char *)(msg + 1); | ||||
| 	log_debug("open read file %d %s", msg->stream, path); | ||||
|  | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, files, &find)) != NULL) { | ||||
| 		error = EBADF; | ||||
| 		goto reply; | ||||
| 	} | ||||
| 	cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata); | ||||
| 	if (cf->closed) { | ||||
| 		error = EBADF; | ||||
| 		goto reply; | ||||
| 	} | ||||
|  | ||||
| 	cf->fd = -1; | ||||
| 	if (msg->fd == -1) | ||||
| 		cf->fd = open(path, flags); | ||||
| 	else if (allow_streams) { | ||||
| 		if (msg->fd != STDIN_FILENO) | ||||
| 			errno = EBADF; | ||||
| 		else { | ||||
| 			cf->fd = dup(msg->fd); | ||||
| 			if (close_received) | ||||
| 				close(msg->fd); /* can only be used once */ | ||||
| 		} | ||||
| 	} else | ||||
| 		errno = EBADF; | ||||
| 	if (cf->fd == -1) { | ||||
| 		error = errno; | ||||
| 		goto reply; | ||||
| 	} | ||||
|  | ||||
| 	cf->event = bufferevent_new(cf->fd, file_read_callback, NULL, | ||||
| 	    file_read_error_callback, cf); | ||||
| 	bufferevent_enable(cf->event, EV_READ); | ||||
| 	return; | ||||
|  | ||||
| reply: | ||||
| 	reply.stream = msg->stream; | ||||
| 	reply.error = error; | ||||
| 	proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply); | ||||
| } | ||||
|  | ||||
| /* Handle a write ready message (server). */ | ||||
| void | ||||
| file_write_ready(struct client_files *files, struct imsg *imsg) | ||||
| { | ||||
| 	struct msg_write_ready	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	struct client_file	 find, *cf; | ||||
|  | ||||
| 	if (msglen != sizeof *msg) | ||||
| 		fatalx("bad MSG_WRITE_READY size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, files, &find)) == NULL) | ||||
| 		return; | ||||
| 	if (msg->error != 0) { | ||||
| 		cf->error = msg->error; | ||||
| 		file_fire_done(cf); | ||||
| 	} else | ||||
| 		file_push(cf); | ||||
| } | ||||
|  | ||||
| /* Handle read data message (server). */ | ||||
| void | ||||
| file_read_data(struct client_files *files, struct imsg *imsg) | ||||
| { | ||||
| 	struct msg_read_data	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	struct client_file	 find, *cf; | ||||
| 	void			*bdata = msg + 1; | ||||
| 	size_t			 bsize = msglen - sizeof *msg; | ||||
|  | ||||
| 	if (msglen < sizeof *msg) | ||||
| 		fatalx("bad MSG_READ_DATA size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, files, &find)) == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	log_debug("file %d read %zu bytes", cf->stream, bsize); | ||||
| 	if (cf->error == 0) { | ||||
| 		if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { | ||||
| 			cf->error = ENOMEM; | ||||
| 			file_fire_done(cf); | ||||
| 		} else | ||||
| 			file_fire_read(cf); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Handle a read done message (server). */ | ||||
| void | ||||
| file_read_done(struct client_files *files, struct imsg *imsg) | ||||
| { | ||||
| 	struct msg_read_done	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	struct client_file	 find, *cf; | ||||
|  | ||||
| 	if (msglen != sizeof *msg) | ||||
| 		fatalx("bad MSG_READ_DONE size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, files, &find)) == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	log_debug("file %d read done", cf->stream); | ||||
| 	cf->error = msg->error; | ||||
| 	file_fire_done(cf); | ||||
| } | ||||
|   | ||||
							
								
								
									
										178
									
								
								format-draw.c
									
									
									
									
									
								
							
							
						
						
									
										178
									
								
								format-draw.c
									
									
									
									
									
								
							| @@ -486,6 +486,18 @@ format_draw_right(struct screen_write_ctx *octx, u_int available, u_int ocx, | ||||
| 	    focus_end, frs); | ||||
| } | ||||
|  | ||||
| /* Draw multiple characters. */ | ||||
| static void | ||||
| format_draw_many(struct screen_write_ctx *ctx, struct style *sy, char ch, | ||||
|     u_int n) | ||||
| { | ||||
| 	u_int	i; | ||||
|  | ||||
| 	utf8_set(&sy->gc.data, ch); | ||||
| 	for (i = 0; i < n; i++) | ||||
| 		screen_write_cell(ctx, &sy->gc); | ||||
| } | ||||
|  | ||||
| /* Draw a format to a screen. */ | ||||
| void | ||||
| format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, | ||||
| @@ -509,10 +521,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, | ||||
| 	size_t			 size = strlen(expanded); | ||||
| 	struct screen		*os = octx->s, s[TOTAL]; | ||||
| 	struct screen_write_ctx	 ctx[TOTAL]; | ||||
| 	u_int			 ocx = os->cx, ocy = os->cy, i, width[TOTAL]; | ||||
| 	u_int			 ocx = os->cx, ocy = os->cy, n, i, width[TOTAL]; | ||||
| 	u_int			 map[] = { LEFT, LEFT, CENTRE, RIGHT }; | ||||
| 	int			 focus_start = -1, focus_end = -1; | ||||
| 	int			 list_state = -1, fill = -1; | ||||
| 	int			 list_state = -1, fill = -1, even; | ||||
| 	enum style_align	 list_align = STYLE_ALIGN_DEFAULT; | ||||
| 	struct grid_cell	 gc, current_default; | ||||
| 	struct style		 sy, saved_sy; | ||||
| @@ -547,6 +559,38 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, | ||||
| 	 */ | ||||
| 	cp = expanded; | ||||
| 	while (*cp != '\0') { | ||||
| 		/* Handle sequences of #. */ | ||||
| 		if (cp[0] == '#' && cp[1] != '[' && cp[1] != '\0') { | ||||
| 			for (n = 1; cp[n] == '#'; n++) | ||||
| 				 /* nothing */; | ||||
| 			even = ((n % 2) == 0); | ||||
| 			if (cp[n] != '[') { | ||||
| 				cp += n; | ||||
| 				if (even) | ||||
| 					n = (n / 2); | ||||
| 				else | ||||
| 					n = (n / 2) + 1; | ||||
| 				width[current] += n; | ||||
| 				format_draw_many(&ctx[current], &sy, '#', n); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (even) | ||||
| 				cp += (n + 1); | ||||
| 			else | ||||
| 				cp += (n - 1); | ||||
| 			if (sy.ignore) | ||||
| 				continue; | ||||
| 			format_draw_many(&ctx[current], &sy, '#', n / 2); | ||||
| 			width[current] += (n / 2); | ||||
| 			if (even) { | ||||
| 				utf8_set(ud, '['); | ||||
| 				screen_write_cell(&ctx[current], &sy.gc); | ||||
| 				width[current]++; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* Is this not a style? */ | ||||
| 		if (cp[0] != '#' || cp[1] != '[' || sy.ignore) { | ||||
| 			/* See if this is a UTF-8 character. */ | ||||
| 			if ((more = utf8_open(ud, *cp)) == UTF8_MORE) { | ||||
| @@ -796,13 +840,33 @@ u_int | ||||
| format_width(const char *expanded) | ||||
| { | ||||
| 	const char		*cp, *end; | ||||
| 	u_int			 width = 0; | ||||
| 	u_int			 n, width = 0; | ||||
| 	struct utf8_data	 ud; | ||||
| 	enum utf8_state		 more; | ||||
|  | ||||
| 	cp = expanded; | ||||
| 	while (*cp != '\0') { | ||||
| 		if (cp[0] == '#' && cp[1] == '[') { | ||||
| 		if (*cp == '#') { | ||||
| 			for (n = 1; cp[n] == '#'; n++) | ||||
| 				/* nothing */; | ||||
| 			if (cp[n] != '[') { | ||||
| 				width += n; | ||||
| 				cp += n; | ||||
| 				continue; | ||||
| 			} | ||||
| 			width += (n / 2); /* one for each ## */ | ||||
|  | ||||
| 			if ((n % 2) == 0) { | ||||
| 				/* | ||||
| 				 * An even number of #s means that all #s are | ||||
| 				 * escaped, so not a style. | ||||
| 				 */ | ||||
| 				width++; /* one for the [ */ | ||||
| 				cp += (n + 1); | ||||
| 				continue; | ||||
| 			} | ||||
| 			cp += (n - 1); /* point to the [ */ | ||||
|  | ||||
| 			end = format_skip(cp + 2, "]"); | ||||
| 			if (end == NULL) | ||||
| 				return (0); | ||||
| @@ -823,19 +887,57 @@ format_width(const char *expanded) | ||||
| 	return (width); | ||||
| } | ||||
|  | ||||
| /* Trim on the left, taking #[] into account. */ | ||||
| /* | ||||
|  * Trim on the left, taking #[] into account.  Note, we copy the whole set of | ||||
|  * unescaped #s, but only add their escaped size to width. This is because the | ||||
|  * format_draw function will actually do the escaping when it runs | ||||
|  */ | ||||
| char * | ||||
| format_trim_left(const char *expanded, u_int limit) | ||||
| { | ||||
| 	char			*copy, *out; | ||||
| 	const char		*cp = expanded, *end; | ||||
| 	u_int			 width = 0; | ||||
| 	u_int			 even, n, width = 0; | ||||
| 	struct utf8_data	 ud; | ||||
| 	enum utf8_state		 more; | ||||
|  | ||||
| 	out = copy = xmalloc(strlen(expanded) + 1); | ||||
| 	out = copy = xcalloc(1, strlen(expanded) + 1); | ||||
| 	while (*cp != '\0') { | ||||
| 		if (cp[0] == '#' && cp[1] == '[') { | ||||
| 		if (width >= limit) | ||||
| 			break; | ||||
| 		if (*cp == '#') { | ||||
| 			for (end = cp + 1; *end == '#'; end++) | ||||
| 				/* nothing */; | ||||
| 			n = end - cp; | ||||
| 			if (*end != '[') { | ||||
| 				if (n > limit - width) | ||||
| 					n = limit - width; | ||||
| 				memcpy(out, cp, n); | ||||
| 				out += n; | ||||
| 				width += n; | ||||
| 				cp = end; | ||||
| 				continue; | ||||
| 			} | ||||
| 			even = ((n % 2) == 0); | ||||
|  | ||||
| 			n /= 2; | ||||
| 			if (n > limit - width) | ||||
| 				n = limit - width; | ||||
| 			width += n; | ||||
| 			n *= 2; | ||||
| 			memcpy(out, cp, n); | ||||
| 			out += n; | ||||
|  | ||||
| 			if (even) { | ||||
| 				if (width + 1 <= limit) { | ||||
| 					*out++ = '['; | ||||
| 					width++; | ||||
| 				} | ||||
| 				cp = end + 1; | ||||
| 				continue; | ||||
| 			} | ||||
| 			cp = end - 1; | ||||
|  | ||||
| 			end = format_skip(cp + 2, "]"); | ||||
| 			if (end == NULL) | ||||
| 				break; | ||||
| @@ -873,7 +975,7 @@ format_trim_right(const char *expanded, u_int limit) | ||||
| { | ||||
| 	char			*copy, *out; | ||||
| 	const char		*cp = expanded, *end; | ||||
| 	u_int			 width = 0, total_width, skip; | ||||
| 	u_int			 width = 0, total_width, skip, old_n, even, n; | ||||
| 	struct utf8_data	 ud; | ||||
| 	enum utf8_state		 more; | ||||
|  | ||||
| @@ -882,12 +984,64 @@ format_trim_right(const char *expanded, u_int limit) | ||||
| 		return (xstrdup(expanded)); | ||||
| 	skip = total_width - limit; | ||||
|  | ||||
| 	out = copy = xmalloc(strlen(expanded) + 1); | ||||
| 	out = copy = xcalloc(1, strlen(expanded) + 1); | ||||
| 	while (*cp != '\0') { | ||||
| 		if (cp[0] == '#' && cp[1] == '[') { | ||||
| 		if (*cp == '#') { | ||||
| 			for (end = cp + 1; *end == '#'; end++) | ||||
| 				/* nothing */; | ||||
| 			old_n = n = end - cp; | ||||
| 			if (*end != '[') { | ||||
| 				if (width <= skip) { | ||||
| 					if (skip - width >= n) | ||||
| 						n = 0; | ||||
| 					else | ||||
| 						n -= (skip - width); | ||||
| 				} | ||||
| 				if (n != 0) { | ||||
| 					memcpy(out, cp, n); | ||||
| 					out += n; | ||||
| 				} | ||||
|  | ||||
| 				/* | ||||
| 				 * The width always increases by the full | ||||
| 				 * amount even if we can't copy anything yet. | ||||
| 				 */ | ||||
| 				width += old_n; | ||||
| 				cp = end; | ||||
| 				continue; | ||||
| 			} | ||||
| 			even = ((n % 2) == 0); | ||||
|  | ||||
| 			n /= 2; | ||||
| 			if (width <= skip) { | ||||
| 				if (skip - width >= n) | ||||
| 					n = 0; | ||||
| 				else | ||||
| 					n -= (skip - width); | ||||
| 			} | ||||
| 			if (n != 0) { | ||||
| 				/* | ||||
| 				 * Copy the full amount because it hasn't been | ||||
| 				 * escaped yet. | ||||
| 				 */ | ||||
| 				memcpy(out, cp, old_n); | ||||
| 				out += old_n; | ||||
| 			} | ||||
| 			cp += old_n; | ||||
| 			width += (old_n / 2) - even; | ||||
|  | ||||
| 			if (even) { | ||||
| 				if (width > skip) | ||||
| 					*out++ = '['; | ||||
| 				width++; | ||||
| 				continue; | ||||
| 			} | ||||
| 			cp = end - 1; | ||||
|  | ||||
| 			end = format_skip(cp + 2, "]"); | ||||
| 			if (end == NULL) | ||||
| 			if (end == NULL) { | ||||
| 				break; | ||||
| 			} | ||||
| 			memcpy(out, cp, end + 1 - cp); | ||||
| 			out += (end + 1 - cp); | ||||
| 			cp = end + 1; | ||||
|   | ||||
							
								
								
									
										89
									
								
								fuzz/input-fuzzer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								fuzz/input-fuzzer.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| /* | ||||
|  * Copyright (c) 2020 Sergey Nizovtsev <snizovtsev@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | ||||
|  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | ||||
|  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include <stddef.h> | ||||
| #include <assert.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| #define FUZZER_MAXLEN 512 | ||||
| #define PANE_WIDTH 80 | ||||
| #define PANE_HEIGHT 25 | ||||
|  | ||||
| struct event_base *libevent; | ||||
|  | ||||
| int | ||||
| LLVMFuzzerTestOneInput(const unsigned char *data, size_t size) | ||||
| { | ||||
| 	struct bufferevent	*vpty[2]; | ||||
| 	struct window		*w; | ||||
| 	struct window_pane 	*wp; | ||||
| 	int			 error; | ||||
|  | ||||
| 	/* | ||||
| 	 * Since AFL doesn't support -max_len paramenter we have to | ||||
| 	 * discard long inputs manually. | ||||
| 	 */ | ||||
| 	if (size > FUZZER_MAXLEN) | ||||
| 		return 0; | ||||
|  | ||||
| 	w = window_create(PANE_WIDTH, PANE_HEIGHT, 0, 0); | ||||
| 	wp = window_add_pane(w, NULL, 0, 0); | ||||
| 	bufferevent_pair_new(libevent, BEV_OPT_CLOSE_ON_FREE, vpty); | ||||
| 	wp->ictx = input_init(wp, vpty[0]); | ||||
| 	window_add_ref(w, __func__); | ||||
|  | ||||
| 	input_parse_buffer(wp, (u_char*) data, size); | ||||
| 	while (cmdq_next(NULL) != 0) | ||||
| 		; | ||||
| 	error = event_base_loop(libevent, EVLOOP_NONBLOCK); | ||||
| 	if (error == -1) | ||||
| 		errx(1, "event_base_loop failed"); | ||||
|  | ||||
| 	assert(w->references == 1); | ||||
| 	window_remove_ref(w, __func__); | ||||
|  | ||||
| 	bufferevent_free(vpty[0]); | ||||
| 	bufferevent_free(vpty[1]); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int | ||||
| LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv) | ||||
| { | ||||
| 	const struct options_table_entry	*oe; | ||||
|  | ||||
| 	global_environ = environ_create(); | ||||
| 	global_options = options_create(NULL); | ||||
| 	global_s_options = options_create(NULL); | ||||
| 	global_w_options = options_create(NULL); | ||||
| 	for (oe = options_table; oe->name != NULL; oe++) { | ||||
| 		if (oe->scope & OPTIONS_TABLE_SERVER) | ||||
| 			options_default(global_options, oe); | ||||
| 		if (oe->scope & OPTIONS_TABLE_SESSION) | ||||
| 			options_default(global_s_options, oe); | ||||
| 		if (oe->scope & OPTIONS_TABLE_WINDOW) | ||||
| 			options_default(global_w_options, oe); | ||||
| 	} | ||||
| 	libevent = osdep_event_init(); | ||||
|  | ||||
| 	options_set_number(global_w_options, "monitor-bell", 0); | ||||
| 	options_set_number(global_w_options, "allow-rename", 1); | ||||
| 	options_set_number(global_options, "set-clipboard", 2); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										8
									
								
								fuzz/input-fuzzer.dict
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								fuzz/input-fuzzer.dict
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| "\x1b[" | ||||
| "1000" | ||||
| "2004" | ||||
| "1049" | ||||
| "38;2" | ||||
| "100;" | ||||
| "tmux;" | ||||
| "rgb:00/00/00" | ||||
							
								
								
									
										2
									
								
								fuzz/input-fuzzer.options
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								fuzz/input-fuzzer.options
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| [libfuzzer] | ||||
| max_len = 512 | ||||
							
								
								
									
										365
									
								
								grid-reader.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								grid-reader.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,365 @@ | ||||
| /* $OpenBSD$ */ | ||||
|  | ||||
| /* | ||||
|  * Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | ||||
|  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | ||||
|  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include "tmux.h" | ||||
| #include <string.h> | ||||
|  | ||||
| /* Initialise virtual cursor. */ | ||||
| void | ||||
| grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy) | ||||
| { | ||||
| 	gr->gd = gd; | ||||
| 	gr->cx = cx; | ||||
| 	gr->cy = cy; | ||||
| } | ||||
|  | ||||
| /* Get cursor position from reader. */ | ||||
| void | ||||
| grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy) | ||||
| { | ||||
| 	*cx = gr->cx; | ||||
| 	*cy = gr->cy; | ||||
| } | ||||
|  | ||||
| /* Get length of line containing the cursor. */ | ||||
| u_int | ||||
| grid_reader_line_length(struct grid_reader *gr) | ||||
| { | ||||
| 	return (grid_line_length(gr->gd, gr->cy)); | ||||
| } | ||||
|  | ||||
| /* Move cursor forward one position. */ | ||||
| void | ||||
| grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all) | ||||
| { | ||||
| 	u_int			px; | ||||
| 	struct grid_cell	gc; | ||||
|  | ||||
| 	if (all) | ||||
| 		px = gr->gd->sx; | ||||
| 	else | ||||
| 		px = grid_reader_line_length(gr); | ||||
|  | ||||
| 	if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) { | ||||
| 		grid_reader_cursor_start_of_line(gr, 0); | ||||
| 		grid_reader_cursor_down(gr); | ||||
| 	} else if (gr->cx < px) { | ||||
| 		gr->cx++; | ||||
| 		while (gr->cx < px) { | ||||
| 			grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); | ||||
| 			if (~gc.flags & GRID_FLAG_PADDING) | ||||
| 				break; | ||||
| 			gr->cx++; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Move cursor back one position. */ | ||||
| void | ||||
| grid_reader_cursor_left(struct grid_reader *gr) | ||||
| { | ||||
| 	struct grid_cell	gc; | ||||
|  | ||||
| 	while (gr->cx > 0) { | ||||
| 		grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); | ||||
| 		if (~gc.flags & GRID_FLAG_PADDING) | ||||
| 			break; | ||||
| 		gr->cx--; | ||||
| 	} | ||||
| 	if (gr->cx == 0 && gr->cy > 0) { | ||||
| 		grid_reader_cursor_up(gr); | ||||
| 		grid_reader_cursor_end_of_line(gr, 0, 0); | ||||
| 	} else if (gr->cx > 0) | ||||
| 		gr->cx--; | ||||
| } | ||||
|  | ||||
| /* Move cursor down one line. */ | ||||
| void | ||||
| grid_reader_cursor_down(struct grid_reader *gr) | ||||
| { | ||||
| 	struct grid_cell	gc; | ||||
|  | ||||
| 	if (gr->cy < gr->gd->hsize + gr->gd->sy - 1) | ||||
| 		gr->cy++; | ||||
| 	while (gr->cx > 0) { | ||||
| 		grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); | ||||
| 		if (~gc.flags & GRID_FLAG_PADDING) | ||||
| 			break; | ||||
| 		gr->cx--; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Move cursor up one line. */ | ||||
| void | ||||
| grid_reader_cursor_up(struct grid_reader *gr) | ||||
| { | ||||
| 	struct grid_cell	gc; | ||||
|  | ||||
| 	if (gr->cy > 0) | ||||
| 		gr->cy--; | ||||
| 	while (gr->cx > 0) { | ||||
| 		grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); | ||||
| 		if (~gc.flags & GRID_FLAG_PADDING) | ||||
| 			break; | ||||
| 		gr->cx--; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Move cursor to the start of the line. */ | ||||
| void | ||||
| grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap) | ||||
| { | ||||
| 	if (wrap) { | ||||
| 		while (gr->cy > 0 && | ||||
| 		    grid_get_line(gr->gd, gr->cy - 1)->flags & | ||||
| 		        GRID_LINE_WRAPPED) | ||||
| 			gr->cy--; | ||||
| 	} | ||||
| 	gr->cx = 0; | ||||
| } | ||||
|  | ||||
| /* Move cursor to the end of the line. */ | ||||
| void | ||||
| grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all) | ||||
| { | ||||
| 	u_int	yy; | ||||
|  | ||||
| 	if (wrap) { | ||||
| 		yy = gr->gd->hsize + gr->gd->sy - 1; | ||||
| 		while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags & | ||||
| 		    GRID_LINE_WRAPPED) | ||||
| 			gr->cy++; | ||||
| 	} | ||||
| 	if (all) | ||||
| 		gr->cx = gr->gd->sx; | ||||
| 	else | ||||
| 		gr->cx = grid_reader_line_length(gr); | ||||
| } | ||||
|  | ||||
| /* Check if character under cursor is in set. */ | ||||
| int | ||||
| grid_reader_in_set(struct grid_reader *gr, const char *set) | ||||
| { | ||||
| 	struct grid_cell	gc; | ||||
|  | ||||
| 	grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); | ||||
| 	if (gc.flags & GRID_FLAG_PADDING) | ||||
| 		return (0); | ||||
| 	return (utf8_cstrhas(set, &gc.data)); | ||||
| } | ||||
|  | ||||
| /* Move cursor to the start of the next word. */ | ||||
| void | ||||
| grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) | ||||
| { | ||||
| 	u_int	xx, yy; | ||||
| 	int expected = 0; | ||||
|  | ||||
| 	/* Do not break up wrapped words. */ | ||||
| 	if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) | ||||
| 		xx = grid_reader_line_length(gr) - 1; | ||||
| 	else | ||||
| 		xx = grid_reader_line_length(gr); | ||||
| 	yy = gr->gd->hsize + gr->gd->sy - 1; | ||||
|  | ||||
| 	/* | ||||
| 	 * If we started inside a word, skip over word characters. Then skip | ||||
| 	 * over separators till the next word. | ||||
| 	 * | ||||
| 	 * expected is initially set to 0 for the former and then 1 for the | ||||
| 	 * latter. It is finally set to 0 when the beginning of the next word is | ||||
| 	 * found. | ||||
| 	 */ | ||||
| 	do { | ||||
| 		while (gr->cx > xx || | ||||
| 		    grid_reader_in_set(gr, separators) == expected) { | ||||
| 			/* Move down if we are past the end of the line. */ | ||||
| 			if (gr->cx > xx) { | ||||
| 				if (gr->cy == yy) | ||||
| 					return; | ||||
| 				grid_reader_cursor_start_of_line(gr, 0); | ||||
| 				grid_reader_cursor_down(gr); | ||||
|  | ||||
| 				if (grid_get_line(gr->gd, gr->cy)->flags & | ||||
| 				    GRID_LINE_WRAPPED) | ||||
| 					xx = grid_reader_line_length(gr) - 1; | ||||
| 				else | ||||
| 					xx = grid_reader_line_length(gr); | ||||
| 			} else | ||||
| 				gr->cx++; | ||||
| 		} | ||||
| 		expected = !expected; | ||||
| 	} while (expected == 1); | ||||
| } | ||||
|  | ||||
| /* Move cursor to the end of the next word. */ | ||||
| void | ||||
| grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) | ||||
| { | ||||
| 	u_int	xx, yy; | ||||
| 	int	expected = 1; | ||||
|  | ||||
| 	/* Do not break up wrapped words. */ | ||||
| 	if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) | ||||
| 		xx = grid_reader_line_length(gr) - 1; | ||||
| 	else | ||||
| 		xx = grid_reader_line_length(gr); | ||||
| 	yy = gr->gd->hsize + gr->gd->sy - 1; | ||||
|  | ||||
| 	/* | ||||
| 	 * If we started on a separator, skip over separators. Then skip over | ||||
| 	 * word characters till the next separator. | ||||
| 	 * | ||||
| 	 * expected is initially set to 1 for the former and then 1 for the | ||||
| 	 * latter. It is finally set to 1 when the end of the next word is | ||||
| 	 * found. | ||||
| 	 */ | ||||
| 	do { | ||||
| 		while (gr->cx > xx || | ||||
| 		    grid_reader_in_set(gr, separators) == expected) { | ||||
| 			/* Move down if we are past the end of the line. */ | ||||
| 			if (gr->cx > xx) { | ||||
| 				if (gr->cy == yy) | ||||
| 					return; | ||||
| 				grid_reader_cursor_start_of_line(gr, 0); | ||||
| 				grid_reader_cursor_down(gr); | ||||
|  | ||||
| 				if (grid_get_line(gr->gd, gr->cy)->flags & | ||||
| 				    GRID_LINE_WRAPPED) | ||||
| 					xx = grid_reader_line_length(gr) - 1; | ||||
| 				else | ||||
| 					xx = grid_reader_line_length(gr); | ||||
| 			} else | ||||
| 				gr->cx++; | ||||
| 		} | ||||
| 		expected = !expected; | ||||
| 	} while (expected == 0); | ||||
| } | ||||
|  | ||||
| /* Move to the previous place where a word begins. */ | ||||
| void | ||||
| grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, | ||||
|     int already) | ||||
| { | ||||
| 	int	oldx, oldy, r; | ||||
|  | ||||
| 	/* Move back to the previous word character. */ | ||||
| 	if (already || grid_reader_in_set(gr, separators)) { | ||||
| 		for (;;) { | ||||
| 			if (gr->cx > 0) { | ||||
| 				gr->cx--; | ||||
| 				if (!grid_reader_in_set(gr, separators)) | ||||
| 					break; | ||||
| 			} else { | ||||
| 				if (gr->cy == 0) | ||||
| 					return; | ||||
| 				grid_reader_cursor_up(gr); | ||||
| 				grid_reader_cursor_end_of_line(gr, 0, 0); | ||||
|  | ||||
| 				/* Stop if separator at EOL. */ | ||||
| 				if (gr->cx > 0) { | ||||
| 					oldx = gr->cx; | ||||
| 					gr->cx--; | ||||
| 					r = grid_reader_in_set(gr, separators); | ||||
| 					gr->cx = oldx; | ||||
| 					if (r) | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Move back to the beginning of this word. */ | ||||
| 	do { | ||||
| 		oldx = gr->cx; | ||||
| 		oldy = gr->cy; | ||||
| 		if (gr->cx == 0) { | ||||
| 			if (gr->cy == 0 || | ||||
| 			  ~grid_get_line(gr->gd, gr->cy - 1)->flags & | ||||
| 			  GRID_LINE_WRAPPED) | ||||
| 				break; | ||||
| 			grid_reader_cursor_up(gr); | ||||
| 			grid_reader_cursor_end_of_line(gr, 0, 0); | ||||
| 		} | ||||
| 		if (gr->cx > 0) | ||||
| 			gr->cx--; | ||||
| 	} while (!grid_reader_in_set(gr, separators)); | ||||
| 	gr->cx = oldx; | ||||
| 	gr->cy = oldy; | ||||
| } | ||||
|  | ||||
| /* Jump forward to character. */ | ||||
| int | ||||
| grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc) | ||||
| { | ||||
| 	struct grid_cell	gc; | ||||
| 	u_int			px, py, xx, yy; | ||||
|  | ||||
| 	px = gr->cx; | ||||
| 	yy = gr->gd->hsize + gr->gd->sy - 1; | ||||
|  | ||||
| 	for (py = gr->cy; py <= yy; py++) { | ||||
| 		xx = grid_line_length(gr->gd, py); | ||||
| 		while (px < xx) { | ||||
| 			grid_get_cell(gr->gd, px, py, &gc); | ||||
| 			if (!(gc.flags & GRID_FLAG_PADDING) && | ||||
| 			    gc.data.size == jc->size && | ||||
| 			    memcmp(gc.data.data, jc->data, gc.data.size) == 0) { | ||||
| 				gr->cx = px; | ||||
| 				gr->cy = py; | ||||
| 				return 1; | ||||
| 			} | ||||
| 			px++; | ||||
| 		} | ||||
|  | ||||
| 		if (py == yy || | ||||
| 		    !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)) | ||||
| 			return 0; | ||||
| 		px = 0; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Jump back to character. */ | ||||
| int | ||||
| grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) | ||||
| { | ||||
| 	struct grid_cell	gc; | ||||
| 	u_int			px, py, xx; | ||||
|  | ||||
| 	xx = gr->cx + 1; | ||||
|  | ||||
| 	for (py = gr->cy + 1; py > 0; py--) { | ||||
| 		for (px = xx; px > 0; px--) { | ||||
| 			grid_get_cell(gr->gd, px - 1, py - 1, &gc); | ||||
| 			if (!(gc.flags & GRID_FLAG_PADDING) && | ||||
| 			    gc.data.size == jc->size && | ||||
| 			    memcmp(gc.data.data, jc->data, gc.data.size) == 0) { | ||||
| 				gr->cx = px - 1; | ||||
| 				gr->cy = py - 1; | ||||
| 				return 1; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (py == 1 || | ||||
| 		    !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED)) | ||||
| 			return 0; | ||||
| 		xx = grid_line_length(gr->gd, py - 2); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										5
									
								
								grid.c
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								grid.c
									
									
									
									
									
								
							| @@ -701,7 +701,6 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg) | ||||
| 		gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Move a group of cells. */ | ||||
| void | ||||
| grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, | ||||
| @@ -1049,14 +1048,14 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, | ||||
| 			    srcl->cellsize * sizeof *dstl->celldata); | ||||
| 		} else | ||||
| 			dstl->celldata = NULL; | ||||
|  | ||||
| 		if (srcl->extdsize != 0) { | ||||
| 			dstl->extdsize = srcl->extdsize; | ||||
| 			dstl->extddata = xreallocarray(NULL, dstl->extdsize, | ||||
| 			    sizeof *dstl->extddata); | ||||
| 			memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * | ||||
| 			    sizeof *dstl->extddata); | ||||
| 		} | ||||
| 		} else | ||||
| 			dstl->extddata = NULL; | ||||
|  | ||||
| 		sy++; | ||||
| 		dy++; | ||||
|   | ||||
							
								
								
									
										142
									
								
								input.c
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								input.c
									
									
									
									
									
								
							| @@ -138,6 +138,8 @@ static void	input_osc_10(struct input_ctx *, const char *); | ||||
| static void	input_osc_11(struct input_ctx *, const char *); | ||||
| static void	input_osc_52(struct input_ctx *, const char *); | ||||
| static void	input_osc_104(struct input_ctx *, const char *); | ||||
| static void	input_osc_110(struct input_ctx *, const char *); | ||||
| static void	input_osc_111(struct input_ctx *, const char *); | ||||
|  | ||||
| /* Transition entry/exit handlers. */ | ||||
| static void	input_clear(struct input_ctx *); | ||||
| @@ -2099,6 +2101,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) | ||||
| 			gc->attr |= GRID_ATTR_UNDERSCORE; | ||||
| 			break; | ||||
| 		case 5: | ||||
| 		case 6: | ||||
| 			gc->attr |= GRID_ATTR_BLINK; | ||||
| 			break; | ||||
| 		case 7: | ||||
| @@ -2110,6 +2113,10 @@ input_csi_dispatch_sgr(struct input_ctx *ictx) | ||||
| 		case 9: | ||||
| 			gc->attr |= GRID_ATTR_STRIKETHROUGH; | ||||
| 			break; | ||||
| 		case 21: | ||||
| 			gc->attr &= ~GRID_ATTR_ALL_UNDERSCORE; | ||||
| 			gc->attr |= GRID_ATTR_UNDERSCORE_2; | ||||
| 			break; | ||||
| 		case 22: | ||||
| 			gc->attr &= ~(GRID_ATTR_BRIGHT|GRID_ATTR_DIM); | ||||
| 			break; | ||||
| @@ -2304,6 +2311,12 @@ input_exit_osc(struct input_ctx *ictx) | ||||
| 	case 104: | ||||
| 		input_osc_104(ictx, p); | ||||
| 		break; | ||||
| 	case 110: | ||||
| 		input_osc_110(ictx, p); | ||||
| 		break; | ||||
| 	case 111: | ||||
| 		input_osc_111(ictx, p); | ||||
| 		break; | ||||
| 	case 112: | ||||
| 		if (*p == '\0') /* no arguments allowed */ | ||||
| 			screen_set_cursor_colour(sctx->s, ""); | ||||
| @@ -2422,50 +2435,43 @@ input_top_bit_set(struct input_ctx *ictx) | ||||
|  | ||||
| /* Parse colour from OSC. */ | ||||
| static int | ||||
| input_osc_parse_colour(const char *p, u_int *r, u_int *g, u_int *b) | ||||
| input_osc_parse_colour(const char *p) | ||||
| { | ||||
| 	u_int		 rsize, gsize, bsize; | ||||
| 	const char	*cp, *s = p; | ||||
| 	double	 c, m, y, k = 0; | ||||
| 	u_int	 r, g, b; | ||||
| 	size_t	 len = strlen(p); | ||||
| 	int	 colour = -1; | ||||
| 	char	*copy; | ||||
|  | ||||
| 	if (sscanf(p, "rgb:%x/%x/%x", r, g, b) != 3) | ||||
| 		return (0); | ||||
| 	p += 4; | ||||
|  | ||||
| 	cp = strchr(p, '/'); | ||||
| 	rsize = cp - p; | ||||
| 	if (rsize == 1) | ||||
| 		(*r) = (*r) | ((*r) << 4); | ||||
| 	else if (rsize == 3) | ||||
| 		(*r) >>= 4; | ||||
| 	else if (rsize == 4) | ||||
| 		(*r) >>= 8; | ||||
| 	else if (rsize != 2) | ||||
| 		return (0); | ||||
|  | ||||
| 	p = cp + 1; | ||||
| 	cp = strchr(p, '/'); | ||||
| 	gsize = cp - p; | ||||
| 	if (gsize == 1) | ||||
| 		(*g) = (*g) | ((*g) << 4); | ||||
| 	else if (gsize == 3) | ||||
| 		(*g) >>= 4; | ||||
| 	else if (gsize == 4) | ||||
| 		(*g) >>= 8; | ||||
| 	else if (gsize != 2) | ||||
| 		return (0); | ||||
|  | ||||
| 	bsize = strlen(cp + 1); | ||||
| 	if (bsize == 1) | ||||
| 		(*b) = (*b) | ((*b) << 4); | ||||
| 	else if (bsize == 3) | ||||
| 		(*b) >>= 4; | ||||
| 	else if (bsize == 4) | ||||
| 		(*b) >>= 8; | ||||
| 	else if (bsize != 2) | ||||
| 		return (0); | ||||
|  | ||||
| 	log_debug("%s: %s = %02x%02x%02x", __func__, s, *r, *g, *b); | ||||
| 	return (1); | ||||
| 	if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) || | ||||
| 	    (len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) || | ||||
| 	    sscanf(p, "%d,%d,%d", &r, &g, &b) == 3) | ||||
| 		colour = colour_join_rgb(r, g, b); | ||||
| 	else if ((len == 18 && | ||||
| 	    sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) || | ||||
| 	    (len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3)) | ||||
| 		colour = colour_join_rgb(r >> 8, g >> 8, b >> 8); | ||||
| 	else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 || | ||||
| 	    sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) && | ||||
| 	    c >= 0 && c <= 1 && m >= 0 && m <= 1 && | ||||
| 	    y >= 0 && y <= 1 && k >= 0 && k <= 1) { | ||||
| 		colour = colour_join_rgb( | ||||
| 		    (1 - c) * (1 - k) * 255, | ||||
| 		    (1 - m) * (1 - k) * 255, | ||||
| 		    (1 - y) * (1 - k) * 255); | ||||
| 	} else { | ||||
| 		while (len != 0 && *p == ' ') { | ||||
| 			p++; | ||||
| 			len--; | ||||
| 		} | ||||
| 		while (len != 0 && p[len - 1] == ' ') | ||||
| 			len--; | ||||
| 		copy = xstrndup(p, len); | ||||
| 		colour = colour_byname(copy); | ||||
| 		free(copy); | ||||
| 	} | ||||
| 	log_debug("%s: %s = %s", __func__, p, colour_tostring(colour)); | ||||
| 	return (colour); | ||||
| } | ||||
|  | ||||
| /* Reply to a colour request. */ | ||||
| @@ -2493,7 +2499,7 @@ input_osc_4(struct input_ctx *ictx, const char *p) | ||||
| 	struct window_pane	*wp = ictx->wp; | ||||
| 	char			*copy, *s, *next = NULL; | ||||
| 	long			 idx; | ||||
| 	u_int			 r, g, b; | ||||
| 	int			 c; | ||||
|  | ||||
| 	if (wp == NULL) | ||||
| 		return; | ||||
| @@ -2507,12 +2513,12 @@ input_osc_4(struct input_ctx *ictx, const char *p) | ||||
| 			goto bad; | ||||
|  | ||||
| 		s = strsep(&next, ";"); | ||||
| 		if (!input_osc_parse_colour(s, &r, &g, &b)) { | ||||
| 		if ((c = input_osc_parse_colour(s)) == -1) { | ||||
| 			s = next; | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		window_pane_set_palette(wp, idx, colour_join_rgb(r, g, b)); | ||||
| 		window_pane_set_palette(wp, idx, c); | ||||
| 		s = next; | ||||
| 	} | ||||
|  | ||||
| @@ -2530,7 +2536,7 @@ input_osc_10(struct input_ctx *ictx, const char *p) | ||||
| { | ||||
| 	struct window_pane	*wp = ictx->wp; | ||||
| 	struct grid_cell	 defaults; | ||||
| 	u_int			 r, g, b; | ||||
| 	int			 c; | ||||
|  | ||||
| 	if (wp == NULL) | ||||
| 		return; | ||||
| @@ -2541,9 +2547,9 @@ input_osc_10(struct input_ctx *ictx, const char *p) | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (!input_osc_parse_colour(p, &r, &g, &b)) | ||||
| 	if ((c = input_osc_parse_colour(p)) == -1) | ||||
| 		goto bad; | ||||
| 	wp->fg = colour_join_rgb(r, g, b); | ||||
| 	wp->fg = c; | ||||
| 	wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); | ||||
|  | ||||
| 	return; | ||||
| @@ -2552,13 +2558,29 @@ bad: | ||||
| 	log_debug("bad OSC 10: %s", p); | ||||
| } | ||||
|  | ||||
| /* Handle the OSC 110 sequence for resetting background colour. */ | ||||
| static void | ||||
| input_osc_110(struct input_ctx *ictx, const char *p) | ||||
| { | ||||
| 	struct window_pane	*wp = ictx->wp; | ||||
|  | ||||
| 	if (wp == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	if (*p != '\0') | ||||
| 		return; | ||||
|  | ||||
| 	wp->fg = 8; | ||||
| 	wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); | ||||
| } | ||||
|  | ||||
| /* Handle the OSC 11 sequence for setting and querying background colour. */ | ||||
| static void | ||||
| input_osc_11(struct input_ctx *ictx, const char *p) | ||||
| { | ||||
| 	struct window_pane	*wp = ictx->wp; | ||||
| 	struct grid_cell	 defaults; | ||||
| 	u_int			 r, g, b; | ||||
| 	int			 c; | ||||
|  | ||||
| 	if (wp == NULL) | ||||
| 		return; | ||||
| @@ -2569,9 +2591,9 @@ input_osc_11(struct input_ctx *ictx, const char *p) | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (!input_osc_parse_colour(p, &r, &g, &b)) | ||||
| 	    goto bad; | ||||
| 	wp->bg = colour_join_rgb(r, g, b); | ||||
| 	if ((c = input_osc_parse_colour(p)) == -1) | ||||
| 		goto bad; | ||||
| 	wp->bg = c; | ||||
| 	wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); | ||||
|  | ||||
| 	return; | ||||
| @@ -2580,6 +2602,22 @@ bad: | ||||
| 	log_debug("bad OSC 11: %s", p); | ||||
| } | ||||
|  | ||||
| /* Handle the OSC 111 sequence for resetting background colour. */ | ||||
| static void | ||||
| input_osc_111(struct input_ctx *ictx, const char *p) | ||||
| { | ||||
| 	struct window_pane	*wp = ictx->wp; | ||||
|  | ||||
| 	if (wp == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	if (*p != '\0') | ||||
| 		return; | ||||
|  | ||||
| 	wp->bg = 8; | ||||
| 	wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED); | ||||
| } | ||||
|  | ||||
| /* Handle the OSC 52 sequence for setting the clipboard. */ | ||||
| static void | ||||
| input_osc_52(struct input_ctx *ictx, const char *p) | ||||
|   | ||||
							
								
								
									
										54
									
								
								job.c
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								job.c
									
									
									
									
									
								
							| @@ -66,19 +66,20 @@ struct job { | ||||
| /* All jobs list. */ | ||||
| static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs); | ||||
|  | ||||
| /* Start a job running, if it isn't already. */ | ||||
| /* Start a job running. */ | ||||
| struct job * | ||||
| job_run(const char *cmd, struct session *s, const char *cwd, | ||||
|     job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, | ||||
|     void *data, int flags, int sx, int sy) | ||||
| job_run(const char *cmd, int argc, char **argv, struct session *s, | ||||
|     const char *cwd, job_update_cb updatecb, job_complete_cb completecb, | ||||
|     job_free_cb freecb, void *data, int flags, int sx, int sy) | ||||
| { | ||||
| 	struct job	*job; | ||||
| 	struct environ	*env; | ||||
| 	pid_t		 pid; | ||||
| 	int		 nullfd, out[2], master; | ||||
| 	const char	*home; | ||||
| 	sigset_t	 set, oldset; | ||||
| 	struct winsize	 ws; | ||||
| 	struct job	 *job; | ||||
| 	struct environ	 *env; | ||||
| 	pid_t		  pid; | ||||
| 	int		  nullfd, out[2], master; | ||||
| 	const char	 *home; | ||||
| 	sigset_t	  set, oldset; | ||||
| 	struct winsize	  ws; | ||||
| 	char		**argvp; | ||||
|  | ||||
| 	/* | ||||
| 	 * Do not set TERM during .tmux.conf, it is nice to be able to use | ||||
| @@ -99,7 +100,13 @@ job_run(const char *cmd, struct session *s, const char *cwd, | ||||
| 			goto fail; | ||||
| 		pid = fork(); | ||||
| 	} | ||||
| 	log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); | ||||
| 	if (cmd == NULL) { | ||||
| 		cmd_log_argv(argc, argv, "%s:", __func__); | ||||
| 		log_debug("%s: cwd=%s", __func__, cwd == NULL ? "" : cwd); | ||||
| 	} else { | ||||
| 		log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, | ||||
| 		    cwd == NULL ? "" : cwd); | ||||
| 	} | ||||
|  | ||||
| 	switch (pid) { | ||||
| 	case -1: | ||||
| @@ -112,10 +119,10 @@ job_run(const char *cmd, struct session *s, const char *cwd, | ||||
| 		proc_clear_signals(server_proc, 1); | ||||
| 		sigprocmask(SIG_SETMASK, &oldset, NULL); | ||||
|  | ||||
| 		if (cwd == NULL || chdir(cwd) != 0) { | ||||
| 			if ((home = find_home()) == NULL || chdir(home) != 0) | ||||
| 				chdir("/"); | ||||
| 		} | ||||
| 		if ((cwd == NULL || chdir(cwd) != 0) && | ||||
| 		    ((home = find_home()) == NULL || chdir(home) != 0) && | ||||
| 		    chdir("/") != 0) | ||||
| 			fatal("chdir failed"); | ||||
|  | ||||
| 		environ_push(env); | ||||
| 		environ_free(env); | ||||
| @@ -139,8 +146,14 @@ job_run(const char *cmd, struct session *s, const char *cwd, | ||||
| 		} | ||||
| 		closefrom(STDERR_FILENO + 1); | ||||
|  | ||||
| 		execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); | ||||
| 		fatal("execl failed"); | ||||
| 		if (cmd != NULL) { | ||||
| 			execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); | ||||
| 			fatal("execl failed"); | ||||
| 		} else { | ||||
| 			argvp = cmd_copy_argv(argc, argv); | ||||
| 			execvp(argvp[0], argvp); | ||||
| 			fatal("execvp failed"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	sigprocmask(SIG_SETMASK, &oldset, NULL); | ||||
| @@ -150,7 +163,10 @@ job_run(const char *cmd, struct session *s, const char *cwd, | ||||
| 	job->state = JOB_RUNNING; | ||||
| 	job->flags = flags; | ||||
|  | ||||
| 	job->cmd = xstrdup(cmd); | ||||
| 	if (cmd != NULL) | ||||
| 		job->cmd = xstrdup(cmd); | ||||
| 	else | ||||
| 		job->cmd = cmd_stringify_argv(argc, argv); | ||||
| 	job->pid = pid; | ||||
| 	job->status = 0; | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								names.c
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								names.c
									
									
									
									
									
								
							| @@ -109,6 +109,8 @@ default_window_name(struct window *w) | ||||
| { | ||||
| 	char	*cmd, *s; | ||||
|  | ||||
| 	if (w->active == NULL) | ||||
| 		return (xstrdup("")); | ||||
| 	cmd = cmd_stringify_argv(w->active->argc, w->active->argv); | ||||
| 	if (cmd != NULL && *cmd != '\0') | ||||
| 		s = parse_window_name(cmd); | ||||
|   | ||||
| @@ -68,6 +68,12 @@ static const char *options_table_set_clipboard_list[] = { | ||||
| static const char *options_table_window_size_list[] = { | ||||
| 	"largest", "smallest", "manual", "latest", NULL | ||||
| }; | ||||
| static const char *options_table_remain_on_exit_list[] = { | ||||
| 	"off", "on", "failed", NULL | ||||
| }; | ||||
| static const char *options_table_detach_on_destroy_list[] = { | ||||
| 	"off", "on", "no-detached", NULL | ||||
| }; | ||||
|  | ||||
| /* Status line format. */ | ||||
| #define OPTIONS_TABLE_STATUS_FORMAT1 \ | ||||
| @@ -401,8 +407,9 @@ const struct options_table_entry options_table[] = { | ||||
| 	}, | ||||
|  | ||||
| 	{ .name = "detach-on-destroy", | ||||
| 	  .type = OPTIONS_TABLE_FLAG, | ||||
| 	  .type = OPTIONS_TABLE_CHOICE, | ||||
| 	  .scope = OPTIONS_TABLE_SESSION, | ||||
| 	  .choices = options_table_detach_on_destroy_list, | ||||
| 	  .default_num = 1, | ||||
| 	  .text = "Whether to detach when a session is destroyed, or switch " | ||||
| 		  "the client to another session if any exist." | ||||
| @@ -948,16 +955,17 @@ const struct options_table_entry options_table[] = { | ||||
| 	}, | ||||
|  | ||||
| 	{ .name = "remain-on-exit", | ||||
| 	  .type = OPTIONS_TABLE_FLAG, | ||||
| 	  .type = OPTIONS_TABLE_CHOICE, | ||||
| 	  .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, | ||||
| 	  .choices = options_table_remain_on_exit_list, | ||||
| 	  .default_num = 0, | ||||
| 	  .text = "Whether panes should remain ('on') or be automatically " | ||||
| 		  "killed ('off') when the program inside exits." | ||||
| 		  "killed ('off' or 'failed') when the program inside exits." | ||||
| 	}, | ||||
|  | ||||
| 	{ .name = "synchronize-panes", | ||||
| 	  .type = OPTIONS_TABLE_FLAG, | ||||
| 	  .scope = OPTIONS_TABLE_WINDOW, | ||||
| 	  .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, | ||||
| 	  .default_num = 0, | ||||
| 	  .text = "Whether typing should be sent to all panes simultaneously." | ||||
| 	}, | ||||
|   | ||||
| @@ -157,8 +157,7 @@ options_value_to_string(struct options_entry *o, union options_value *ov, | ||||
| 		case OPTIONS_TABLE_CHOICE: | ||||
| 			s = xstrdup(o->tableentry->choices[ov->number]); | ||||
| 			break; | ||||
| 		case OPTIONS_TABLE_STRING: | ||||
| 		case OPTIONS_TABLE_COMMAND: | ||||
| 		default: | ||||
| 			fatalx("not a number option type"); | ||||
| 		} | ||||
| 		return (s); | ||||
| @@ -311,6 +310,8 @@ options_default_to_string(const struct options_table_entry *oe) | ||||
| 	case OPTIONS_TABLE_CHOICE: | ||||
| 		s = xstrdup(oe->choices[oe->default_num]); | ||||
| 		break; | ||||
| 	default: | ||||
| 		fatalx("unknown option type"); | ||||
| 	} | ||||
| 	return (s); | ||||
| } | ||||
| @@ -703,7 +704,7 @@ options_get_number(struct options *oo, const char *name) | ||||
| 	if (o == NULL) | ||||
| 		fatalx("missing option %s", name); | ||||
| 	if (!OPTIONS_IS_NUMBER(o)) | ||||
| 	    fatalx("option %s is not a number", name); | ||||
| 		fatalx("option %s is not a number", name); | ||||
| 	return (o->value.number); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| #include <sys/param.h> | ||||
| #include <sys/stat.h> | ||||
|  | ||||
| #include <event.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
|   | ||||
| @@ -20,12 +20,13 @@ | ||||
| #include <sys/sysctl.h> | ||||
|  | ||||
| #include <Availability.h> | ||||
| #include <event.h> | ||||
| #include <libproc.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "compat.h" | ||||
|  | ||||
| char			*osdep_get_name(int, char *); | ||||
| char			*osdep_get_cwd(int); | ||||
| struct event_base	*osdep_event_init(void); | ||||
|   | ||||
| @@ -23,7 +23,6 @@ | ||||
|  | ||||
| #include <err.h> | ||||
| #include <errno.h> | ||||
| #include <event.h> | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|   | ||||
| @@ -24,7 +24,6 @@ | ||||
|  | ||||
| #include <err.h> | ||||
| #include <errno.h> | ||||
| #include <event.h> | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|   | ||||
							
								
								
									
										52
									
								
								osdep-haiku.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								osdep-haiku.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| /* $OpenBSD$ */ | ||||
|  | ||||
| /* | ||||
|  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
|  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | ||||
|  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | ||||
|  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <unistd.h> | ||||
| #include <kernel/OS.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| char * | ||||
| osdep_get_name(int fd, __unused char *tty) | ||||
| { | ||||
| 	pid_t		tid; | ||||
| 	team_info	tinfo; | ||||
|  | ||||
| 	if ((tid = tcgetpgrp(fd)) == -1) | ||||
| 		return (NULL); | ||||
|  | ||||
| 	if (get_team_info(tid, &tinfo) != B_OK) | ||||
| 		return (NULL); | ||||
|  | ||||
| 	/* Up to the first 64 characters. */ | ||||
| 	return (xstrdup(tinfo.args)); | ||||
| } | ||||
|  | ||||
| char * | ||||
| osdep_get_cwd(int fd) | ||||
| { | ||||
| 	return (NULL); | ||||
| } | ||||
|  | ||||
| struct event_base * | ||||
| osdep_event_init(void) | ||||
| { | ||||
| 	return (event_init()); | ||||
| } | ||||
| @@ -18,8 +18,6 @@ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <event.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| char * | ||||
|   | ||||
| @@ -20,7 +20,6 @@ | ||||
| #include <sys/stat.h> | ||||
| #include <sys/param.h> | ||||
|  | ||||
| #include <event.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
|   | ||||
| @@ -22,7 +22,6 @@ | ||||
| #include <sys/sysctl.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <event.h> | ||||
| #include <limits.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
|   | ||||
| @@ -23,11 +23,12 @@ | ||||
| #include <sys/stat.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <event.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "compat.h" | ||||
|  | ||||
| #ifndef nitems | ||||
| #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) | ||||
| #endif | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| #include <sys/param.h> | ||||
| #include <sys/stat.h> | ||||
|  | ||||
| #include <event.h> | ||||
| #include <fcntl.h> | ||||
| #include <procfs.h> | ||||
| #include <stdio.h> | ||||
|   | ||||
| @@ -18,8 +18,6 @@ | ||||
|  | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <event.h> | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| char * | ||||
|   | ||||
							
								
								
									
										211
									
								
								popup.c
									
									
									
									
									
								
							
							
						
						
									
										211
									
								
								popup.c
									
									
									
									
									
								
							| @@ -31,13 +31,7 @@ struct popup_data { | ||||
| 	struct cmdq_item	 *item; | ||||
| 	int			  flags; | ||||
|  | ||||
| 	char			**lines; | ||||
| 	u_int			  nlines; | ||||
|  | ||||
| 	char			 *cmd; | ||||
| 	struct cmd_find_state	  fs; | ||||
| 	struct screen		  s; | ||||
|  | ||||
| 	struct job		 *job; | ||||
| 	struct input_ctx	 *ictx; | ||||
| 	int			  status; | ||||
| @@ -104,54 +98,11 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) | ||||
| 	ttyctx->arg = pd; | ||||
| } | ||||
|  | ||||
| static void | ||||
| popup_write_screen(struct client *c, struct popup_data *pd) | ||||
| { | ||||
| 	struct cmdq_item	*item = pd->item; | ||||
| 	struct screen_write_ctx	 ctx; | ||||
| 	char			*copy, *next, *loop, *tmp; | ||||
| 	struct format_tree	*ft; | ||||
| 	u_int			 i, y; | ||||
|  | ||||
| 	ft = format_create(c, item, FORMAT_NONE, 0); | ||||
| 	if (cmd_find_valid_state(&pd->fs)) | ||||
| 		format_defaults(ft, c, pd->fs.s, pd->fs.wl, pd->fs.wp); | ||||
| 	else | ||||
| 		format_defaults(ft, c, NULL, NULL, NULL); | ||||
|  | ||||
| 	screen_write_start(&ctx, &pd->s); | ||||
| 	screen_write_clearscreen(&ctx, 8); | ||||
|  | ||||
| 	y = 0; | ||||
| 	for (i = 0; i < pd->nlines; i++) { | ||||
| 		if (y == pd->sy - 2) | ||||
| 			break; | ||||
| 		copy = next = xstrdup(pd->lines[i]); | ||||
| 		while ((loop = strsep(&next, "\n")) != NULL) { | ||||
| 			if (y == pd->sy - 2) | ||||
| 				break; | ||||
| 			tmp = format_expand(ft, loop); | ||||
| 			screen_write_cursormove(&ctx, 0, y, 0); | ||||
| 			format_draw(&ctx, &grid_default_cell, pd->sx - 2, tmp, | ||||
| 			    NULL); | ||||
| 			free(tmp); | ||||
| 			y++; | ||||
| 		} | ||||
| 		free(copy); | ||||
| 	} | ||||
|  | ||||
| 	format_free(ft); | ||||
| 	screen_write_cursormove(&ctx, 0, y, 0); | ||||
| 	screen_write_stop(&ctx); | ||||
| } | ||||
|  | ||||
| static struct screen * | ||||
| popup_mode_cb(struct client *c, u_int *cx, u_int *cy) | ||||
| { | ||||
| 	struct popup_data	*pd = c->overlay_data; | ||||
|  | ||||
| 	if (pd->ictx == NULL) | ||||
| 		return (0); | ||||
| 	*cx = pd->px + 1 + pd->s.cx; | ||||
| 	*cy = pd->py + 1 + pd->s.cy; | ||||
| 	return (&pd->s); | ||||
| @@ -199,14 +150,12 @@ popup_free_cb(struct client *c) | ||||
| { | ||||
| 	struct popup_data	*pd = c->overlay_data; | ||||
| 	struct cmdq_item	*item = pd->item; | ||||
| 	u_int			 i; | ||||
|  | ||||
| 	if (pd->cb != NULL) | ||||
| 		pd->cb(pd->status, pd->arg); | ||||
|  | ||||
| 	if (item != NULL) { | ||||
| 		if (pd->ictx != NULL && | ||||
| 		    cmdq_get_client(item) != NULL && | ||||
| 		if (cmdq_get_client(item) != NULL && | ||||
| 		    cmdq_get_client(item)->session == NULL) | ||||
| 			cmdq_get_client(item)->retval = pd->status; | ||||
| 		cmdq_continue(item); | ||||
| @@ -215,15 +164,9 @@ popup_free_cb(struct client *c) | ||||
|  | ||||
| 	if (pd->job != NULL) | ||||
| 		job_free(pd->job); | ||||
| 	if (pd->ictx != NULL) | ||||
| 		input_free(pd->ictx); | ||||
|  | ||||
| 	for (i = 0; i < pd->nlines; i++) | ||||
| 		free(pd->lines[i]); | ||||
| 	free(pd->lines); | ||||
| 	input_free(pd->ictx); | ||||
|  | ||||
| 	screen_free(&pd->s); | ||||
| 	free(pd->cmd); | ||||
| 	free(pd); | ||||
| } | ||||
|  | ||||
| @@ -262,9 +205,7 @@ popup_handle_drag(struct client *c, struct popup_data *pd, | ||||
| 		pd->sy = m->y - pd->py; | ||||
|  | ||||
| 		screen_resize(&pd->s, pd->sx - 2, pd->sy - 2, 0); | ||||
| 		if (pd->ictx == NULL) | ||||
| 			popup_write_screen(c, pd); | ||||
| 		else if (pd->job != NULL) | ||||
| 		if (pd->job != NULL) | ||||
| 			job_resize(pd->job, pd->sx - 2, pd->sy - 2); | ||||
| 		server_redraw_client(c); | ||||
| 	} | ||||
| @@ -275,13 +216,8 @@ popup_key_cb(struct client *c, struct key_event *event) | ||||
| { | ||||
| 	struct popup_data	*pd = c->overlay_data; | ||||
| 	struct mouse_event	*m = &event->m; | ||||
| 	struct cmd_find_state	*fs = &pd->fs; | ||||
| 	struct format_tree	*ft; | ||||
| 	const char		*cmd, *buf; | ||||
| 	const char		*buf; | ||||
| 	size_t			 len; | ||||
| 	struct cmdq_state	*state; | ||||
| 	enum cmd_parse_status	 status; | ||||
| 	char			*error; | ||||
|  | ||||
| 	if (KEYC_IS_MOUSE(event->key)) { | ||||
| 		if (pd->dragging != OFF) { | ||||
| @@ -313,56 +249,22 @@ popup_key_cb(struct client *c, struct key_event *event) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (pd->ictx != NULL && (pd->flags & POPUP_WRITEKEYS)) { | ||||
| 		if (((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0 || | ||||
| 		    pd->job == NULL) && | ||||
| 		    (event->key == '\033' || event->key == '\003')) | ||||
| 			return (1); | ||||
| 		if (pd->job == NULL) | ||||
| 			return (0); | ||||
| 	if ((((pd->flags & (POPUP_CLOSEEXIT|POPUP_CLOSEEXITZERO)) == 0) || | ||||
| 	    pd->job == NULL) && | ||||
| 	    (event->key == '\033' || event->key == '\003')) | ||||
| 		return (1); | ||||
| 	if (pd->job != NULL) { | ||||
| 		if (KEYC_IS_MOUSE(event->key)) { | ||||
| 			/* Must be inside, checked already. */ | ||||
| 			if (!input_key_get_mouse(&pd->s, m, m->x - pd->px, | ||||
| 			    m->y - pd->py, &buf, &len)) | ||||
| 			if (!input_key_get_mouse(&pd->s, m, m->x - pd->px - 1, | ||||
| 			    m->y - pd->py - 1, &buf, &len)) | ||||
| 				return (0); | ||||
| 			bufferevent_write(job_get_event(pd->job), buf, len); | ||||
| 			return (0); | ||||
| 		} | ||||
| 		input_key(&pd->s, job_get_event(pd->job), event->key); | ||||
| 		return (0); | ||||
| 	} | ||||
|  | ||||
| 	if (pd->cmd == NULL) | ||||
| 		return (1); | ||||
|  | ||||
| 	ft = format_create(NULL, pd->item, FORMAT_NONE, 0); | ||||
| 	if (cmd_find_valid_state(fs)) | ||||
| 		format_defaults(ft, c, fs->s, fs->wl, fs->wp); | ||||
| 	else | ||||
| 		format_defaults(ft, c, NULL, NULL, NULL); | ||||
| 	format_add(ft, "popup_key", "%s", key_string_lookup_key(event->key, 0)); | ||||
| 	if (KEYC_IS_MOUSE(event->key)) { | ||||
| 		format_add(ft, "popup_mouse", "1"); | ||||
| 		format_add(ft, "popup_mouse_x", "%u", m->x - pd->px); | ||||
| 		format_add(ft, "popup_mouse_y", "%u", m->y - pd->py); | ||||
| 	} | ||||
| 	cmd = format_expand(ft, pd->cmd); | ||||
| 	format_free(ft); | ||||
|  | ||||
| 	if (pd->item != NULL) | ||||
| 		event = cmdq_get_event(pd->item); | ||||
| 	else | ||||
| 		event = NULL; | ||||
| 	state = cmdq_new_state(&pd->fs, event, 0); | ||||
|  | ||||
| 	status = cmd_parse_and_append(cmd, NULL, c, state, &error); | ||||
| 	if (status == CMD_PARSE_ERROR) { | ||||
| 		cmdq_append(c, cmdq_get_error(error)); | ||||
| 		free(error); | ||||
| 	} | ||||
| 	cmdq_free_state(state); | ||||
|  | ||||
| 	return (1); | ||||
| 	return (0); | ||||
|  | ||||
| out: | ||||
| 	pd->lx = m->x; | ||||
| @@ -415,62 +317,12 @@ popup_job_complete_cb(struct job *job) | ||||
| 		server_client_clear_overlay(pd->c); | ||||
| } | ||||
|  | ||||
| u_int | ||||
| popup_height(u_int nlines, const char **lines) | ||||
| { | ||||
| 	char	*copy, *next, *loop; | ||||
| 	u_int	 i, height = 0; | ||||
|  | ||||
| 	for (i = 0; i < nlines; i++) { | ||||
| 		copy = next = xstrdup(lines[i]); | ||||
| 		while ((loop = strsep(&next, "\n")) != NULL) | ||||
| 			height++; | ||||
| 		free(copy); | ||||
| 	} | ||||
|  | ||||
| 	return (height); | ||||
| } | ||||
|  | ||||
| u_int | ||||
| popup_width(struct cmdq_item *item, u_int nlines, const char **lines, | ||||
|     struct client *c, struct cmd_find_state *fs) | ||||
| { | ||||
| 	char			*copy, *next, *loop, *tmp; | ||||
| 	struct format_tree	*ft; | ||||
| 	u_int			 i, width = 0, tmpwidth; | ||||
|  | ||||
| 	ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); | ||||
| 	if (fs != NULL && cmd_find_valid_state(fs)) | ||||
| 		format_defaults(ft, c, fs->s, fs->wl, fs->wp); | ||||
| 	else | ||||
| 		format_defaults(ft, c, NULL, NULL, NULL); | ||||
|  | ||||
| 	for (i = 0; i < nlines; i++) { | ||||
| 		copy = next = xstrdup(lines[i]); | ||||
| 		while ((loop = strsep(&next, "\n")) != NULL) { | ||||
| 			tmp = format_expand(ft, loop); | ||||
| 			tmpwidth = format_width(tmp); | ||||
| 			if (tmpwidth > width) | ||||
| 				width = tmpwidth; | ||||
| 			free(tmp); | ||||
| 		} | ||||
| 		free(copy); | ||||
| 	} | ||||
|  | ||||
| 	format_free(ft); | ||||
| 	return (width); | ||||
| } | ||||
|  | ||||
| int | ||||
| popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, | ||||
|     u_int sy, u_int nlines, const char **lines, const char *shellcmd, | ||||
|     const char *cmd, const char *cwd, struct client *c, | ||||
|     struct cmd_find_state *fs, popup_close_cb cb, void *arg) | ||||
|     u_int sy, const char *shellcmd, int argc, char **argv, const char *cwd, | ||||
|     struct client *c, struct session *s, popup_close_cb cb, void *arg) | ||||
| { | ||||
| 	struct popup_data	*pd; | ||||
| 	u_int			 i; | ||||
| 	struct session		*s; | ||||
| 	int			 jobflags; | ||||
|  | ||||
| 	if (sx < 3 || sy < 3) | ||||
| 		return (-1); | ||||
| @@ -488,39 +340,17 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, | ||||
| 	pd->arg = arg; | ||||
| 	pd->status = 128 + SIGHUP; | ||||
|  | ||||
| 	if (fs != NULL) | ||||
| 		cmd_find_copy_state(&pd->fs, fs); | ||||
| 	screen_init(&pd->s, sx - 2, sy - 2, 0); | ||||
|  | ||||
| 	if (cmd != NULL) | ||||
| 		pd->cmd = xstrdup(cmd); | ||||
|  | ||||
| 	pd->px = px; | ||||
| 	pd->py = py; | ||||
| 	pd->sx = sx; | ||||
| 	pd->sy = sy; | ||||
|  | ||||
| 	pd->nlines = nlines; | ||||
| 	if (pd->nlines != 0) | ||||
| 		pd->lines = xreallocarray(NULL, pd->nlines, sizeof *pd->lines); | ||||
|  | ||||
| 	for (i = 0; i < pd->nlines; i++) | ||||
| 		pd->lines[i] = xstrdup(lines[i]); | ||||
| 	popup_write_screen(c, pd); | ||||
|  | ||||
| 	if (shellcmd != NULL) { | ||||
| 		if (fs != NULL) | ||||
| 			s = fs->s; | ||||
| 		else | ||||
| 			s = NULL; | ||||
| 		jobflags = JOB_NOWAIT|JOB_PTY; | ||||
| 		if (flags & POPUP_WRITEKEYS) | ||||
| 		    jobflags |= JOB_KEEPWRITE; | ||||
| 		pd->job = job_run(shellcmd, s, cwd, popup_job_update_cb, | ||||
| 		    popup_job_complete_cb, NULL, pd, jobflags, pd->sx - 2, | ||||
| 		    pd->sy - 2); | ||||
| 		pd->ictx = input_init(NULL, job_get_event(pd->job)); | ||||
| 	} | ||||
| 	pd->job = job_run(shellcmd, argc, argv, s, cwd, | ||||
| 	    popup_job_update_cb, popup_job_complete_cb, NULL, pd, | ||||
| 	    JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE, pd->sx - 2, pd->sy - 2); | ||||
| 	pd->ictx = input_init(NULL, job_get_event(pd->job)); | ||||
|  | ||||
| 	server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb, | ||||
| 	    popup_draw_cb, popup_key_cb, popup_free_cb, pd); | ||||
| @@ -606,9 +436,8 @@ popup_editor(struct client *c, const char *buf, size_t len, | ||||
| 	py = (c->tty.sy / 2) - (sy / 2); | ||||
|  | ||||
| 	xasprintf(&cmd, "%s %s", editor, path); | ||||
| 	if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, | ||||
| 	    0, NULL, cmd, NULL, _PATH_TMP, c, NULL, popup_editor_close_cb, | ||||
| 	    pe) != 0) { | ||||
| 	if (popup_display(POPUP_CLOSEEXIT, NULL, px, py, sx, sy, cmd, 0, NULL, | ||||
| 	    _PATH_TMP, c, NULL, popup_editor_close_cb, pe) != 0) { | ||||
| 		popup_editor_free(pe); | ||||
| 		free(cmd); | ||||
| 		return (-1); | ||||
|   | ||||
							
								
								
									
										38
									
								
								proc.c
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								proc.c
									
									
									
									
									
								
							| @@ -17,11 +17,12 @@ | ||||
|  */ | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/queue.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/uio.h> | ||||
| #include <sys/utsname.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <event.h> | ||||
| #include <signal.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| @@ -47,6 +48,8 @@ struct tmuxproc { | ||||
| 	struct event	  ev_sigusr1; | ||||
| 	struct event	  ev_sigusr2; | ||||
| 	struct event	  ev_sigwinch; | ||||
|  | ||||
| 	TAILQ_HEAD(, tmuxpeer) peers; | ||||
| }; | ||||
|  | ||||
| struct tmuxpeer { | ||||
| @@ -60,6 +63,8 @@ struct tmuxpeer { | ||||
|  | ||||
| 	void		(*dispatchcb)(struct imsg *, void *); | ||||
| 	void		 *arg; | ||||
|  | ||||
| 	TAILQ_ENTRY(tmuxpeer) entry; | ||||
| }; | ||||
|  | ||||
| static int	peer_check_version(struct tmuxpeer *, struct imsg *); | ||||
| @@ -203,6 +208,7 @@ proc_start(const char *name) | ||||
|  | ||||
| 	tp = xcalloc(1, sizeof *tp); | ||||
| 	tp->name = xstrdup(name); | ||||
| 	TAILQ_INIT(&tp->peers); | ||||
|  | ||||
| 	return (tp); | ||||
| } | ||||
| @@ -220,6 +226,10 @@ proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) | ||||
| void | ||||
| proc_exit(struct tmuxproc *tp) | ||||
| { | ||||
| 	struct tmuxpeer	*peer; | ||||
|  | ||||
| 	TAILQ_FOREACH(peer, &tp->peers, entry) | ||||
| 	    imsg_flush(&peer->ibuf); | ||||
| 	tp->exit = 1; | ||||
| } | ||||
|  | ||||
| @@ -310,6 +320,7 @@ proc_add_peer(struct tmuxproc *tp, int fd, | ||||
| 	event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); | ||||
|  | ||||
| 	log_debug("add peer %p: %d (%p)", peer, fd, arg); | ||||
| 	TAILQ_INSERT_TAIL(&tp->peers, peer, entry); | ||||
|  | ||||
| 	proc_update_event(peer); | ||||
| 	return (peer); | ||||
| @@ -318,6 +329,7 @@ proc_add_peer(struct tmuxproc *tp, int fd, | ||||
| void | ||||
| proc_remove_peer(struct tmuxpeer *peer) | ||||
| { | ||||
| 	TAILQ_REMOVE(&peer->parent->peers, peer, entry); | ||||
| 	log_debug("remove peer %p", peer); | ||||
|  | ||||
| 	event_del(&peer->event); | ||||
| @@ -338,3 +350,27 @@ proc_toggle_log(struct tmuxproc *tp) | ||||
| { | ||||
| 	log_toggle(tp->name); | ||||
| } | ||||
|  | ||||
| pid_t | ||||
| proc_fork_and_daemon(int *fd) | ||||
| { | ||||
| 	pid_t	pid; | ||||
| 	int	pair[2]; | ||||
|  | ||||
| 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) | ||||
| 		fatal("socketpair failed"); | ||||
| 	switch (pid = fork()) { | ||||
| 	case -1: | ||||
| 		fatal("fork failed"); | ||||
| 	case 0: | ||||
| 		close(pair[0]); | ||||
| 		*fd = pair[1]; | ||||
| 		if (daemon(1, 0) != 0) | ||||
| 			fatal("daemon failed"); | ||||
| 		return (0); | ||||
| 	default: | ||||
| 		close(pair[1]); | ||||
| 		*fd = pair[0]; | ||||
| 		return (pid); | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										243
									
								
								regress/osc-11colours.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								regress/osc-11colours.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,243 @@ | ||||
| #!/bin/sh | ||||
|  | ||||
| PATH=/bin:/usr/bin | ||||
| TERM=screen | ||||
|  | ||||
| [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) | ||||
| TMUX="$TEST_TMUX -Ltest" | ||||
| $TMUX kill-server 2>/dev/null | ||||
|  | ||||
| $TMUX -vv new -d | ||||
| $TMUX set -g remain-on-exit on | ||||
|  | ||||
| do_test() { | ||||
| 	$TMUX splitw "printf '$1'" | ||||
| 	c="$($TMUX display -p '#{pane_bg}')" | ||||
| 	$TMUX kill-pane | ||||
| 	[ "$c" != "$2" ] && return 1 | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| do_test '\033]11;rgb:ff/ff/ff\007' '#ffffff' || exit 1 | ||||
| do_test '\033]11;rgb:ff/ff/ff\007\033]111\007' 'default' || exit 1 | ||||
|  | ||||
| do_test '\033]11;cmy:0.9373/0.6941/0.4549\007' '#0f4e8b' || exit 1 | ||||
| do_test '\033]11;cmyk:0.88/0.44/0.00/0.45\007' '#104e8c' || exit 1 | ||||
|  | ||||
| do_test '\033]11;16,78,139\007' '#104e8b' || exit 1 | ||||
| do_test '\033]11;#104E8B\007' '#104e8b' || exit 1 | ||||
| do_test '\033]11;#10004E008B00\007' '#104e8b' || exit 1 | ||||
| do_test '\033]11;DodgerBlue4\007' '#104e8b' || exit 1 | ||||
| do_test '\033]11;DodgerBlue4    \007' '#104e8b' || exit 1 | ||||
| do_test '\033]11;    DodgerBlue4\007' '#104e8b' || exit 1 | ||||
| do_test '\033]11;rgb:10/4E/8B\007' '#104e8b' || exit 1 | ||||
| do_test '\033]11;rgb:1000/4E00/8B00\007' '#104e8b' || exit 1 | ||||
|  | ||||
| do_test '\033]11;grey\007' '#bebebe' || exit 1 | ||||
| do_test '\033]11;grey0\007' '#000000' || exit 1 | ||||
| do_test '\033]11;grey1\007' '#030303' || exit 1 | ||||
| do_test '\033]11;grey2\007' '#050505' || exit 1 | ||||
| do_test '\033]11;grey3\007' '#080808' || exit 1 | ||||
| do_test '\033]11;grey4\007' '#0a0a0a' || exit 1 | ||||
| do_test '\033]11;grey5\007' '#0d0d0d' || exit 1 | ||||
| do_test '\033]11;grey6\007' '#0f0f0f' || exit 1 | ||||
| do_test '\033]11;grey7\007' '#121212' || exit 1 | ||||
| do_test '\033]11;grey8\007' '#141414' || exit 1 | ||||
| do_test '\033]11;grey9\007' '#171717' || exit 1 | ||||
| do_test '\033]11;grey10\007' '#1a1a1a' || exit 1 | ||||
| do_test '\033]11;grey11\007' '#1c1c1c' || exit 1 | ||||
| do_test '\033]11;grey12\007' '#1f1f1f' || exit 1 | ||||
| do_test '\033]11;grey13\007' '#212121' || exit 1 | ||||
| do_test '\033]11;grey14\007' '#242424' || exit 1 | ||||
| do_test '\033]11;grey15\007' '#262626' || exit 1 | ||||
| do_test '\033]11;grey16\007' '#292929' || exit 1 | ||||
| do_test '\033]11;grey17\007' '#2b2b2b' || exit 1 | ||||
| do_test '\033]11;grey18\007' '#2e2e2e' || exit 1 | ||||
| do_test '\033]11;grey19\007' '#303030' || exit 1 | ||||
| do_test '\033]11;grey20\007' '#333333' || exit 1 | ||||
| do_test '\033]11;grey21\007' '#363636' || exit 1 | ||||
| do_test '\033]11;grey22\007' '#383838' || exit 1 | ||||
| do_test '\033]11;grey23\007' '#3b3b3b' || exit 1 | ||||
| do_test '\033]11;grey24\007' '#3d3d3d' || exit 1 | ||||
| do_test '\033]11;grey25\007' '#404040' || exit 1 | ||||
| do_test '\033]11;grey26\007' '#424242' || exit 1 | ||||
| do_test '\033]11;grey27\007' '#454545' || exit 1 | ||||
| do_test '\033]11;grey28\007' '#474747' || exit 1 | ||||
| do_test '\033]11;grey29\007' '#4a4a4a' || exit 1 | ||||
| do_test '\033]11;grey30\007' '#4d4d4d' || exit 1 | ||||
| do_test '\033]11;grey31\007' '#4f4f4f' || exit 1 | ||||
| do_test '\033]11;grey32\007' '#525252' || exit 1 | ||||
| do_test '\033]11;grey33\007' '#545454' || exit 1 | ||||
| do_test '\033]11;grey34\007' '#575757' || exit 1 | ||||
| do_test '\033]11;grey35\007' '#595959' || exit 1 | ||||
| do_test '\033]11;grey36\007' '#5c5c5c' || exit 1 | ||||
| do_test '\033]11;grey37\007' '#5e5e5e' || exit 1 | ||||
| do_test '\033]11;grey38\007' '#616161' || exit 1 | ||||
| do_test '\033]11;grey39\007' '#636363' || exit 1 | ||||
| do_test '\033]11;grey40\007' '#666666' || exit 1 | ||||
| do_test '\033]11;grey41\007' '#696969' || exit 1 | ||||
| do_test '\033]11;grey42\007' '#6b6b6b' || exit 1 | ||||
| do_test '\033]11;grey43\007' '#6e6e6e' || exit 1 | ||||
| do_test '\033]11;grey44\007' '#707070' || exit 1 | ||||
| do_test '\033]11;grey45\007' '#737373' || exit 1 | ||||
| do_test '\033]11;grey46\007' '#757575' || exit 1 | ||||
| do_test '\033]11;grey47\007' '#787878' || exit 1 | ||||
| do_test '\033]11;grey48\007' '#7a7a7a' || exit 1 | ||||
| do_test '\033]11;grey49\007' '#7d7d7d' || exit 1 | ||||
| do_test '\033]11;grey50\007' '#7f7f7f' || exit 1 | ||||
| do_test '\033]11;grey51\007' '#828282' || exit 1 | ||||
| do_test '\033]11;grey52\007' '#858585' || exit 1 | ||||
| do_test '\033]11;grey53\007' '#878787' || exit 1 | ||||
| do_test '\033]11;grey54\007' '#8a8a8a' || exit 1 | ||||
| do_test '\033]11;grey55\007' '#8c8c8c' || exit 1 | ||||
| do_test '\033]11;grey56\007' '#8f8f8f' || exit 1 | ||||
| do_test '\033]11;grey57\007' '#919191' || exit 1 | ||||
| do_test '\033]11;grey58\007' '#949494' || exit 1 | ||||
| do_test '\033]11;grey59\007' '#969696' || exit 1 | ||||
| do_test '\033]11;grey60\007' '#999999' || exit 1 | ||||
| do_test '\033]11;grey61\007' '#9c9c9c' || exit 1 | ||||
| do_test '\033]11;grey62\007' '#9e9e9e' || exit 1 | ||||
| do_test '\033]11;grey63\007' '#a1a1a1' || exit 1 | ||||
| do_test '\033]11;grey64\007' '#a3a3a3' || exit 1 | ||||
| do_test '\033]11;grey65\007' '#a6a6a6' || exit 1 | ||||
| do_test '\033]11;grey66\007' '#a8a8a8' || exit 1 | ||||
| do_test '\033]11;grey67\007' '#ababab' || exit 1 | ||||
| do_test '\033]11;grey68\007' '#adadad' || exit 1 | ||||
| do_test '\033]11;grey69\007' '#b0b0b0' || exit 1 | ||||
| do_test '\033]11;grey70\007' '#b3b3b3' || exit 1 | ||||
| do_test '\033]11;grey71\007' '#b5b5b5' || exit 1 | ||||
| do_test '\033]11;grey72\007' '#b8b8b8' || exit 1 | ||||
| do_test '\033]11;grey73\007' '#bababa' || exit 1 | ||||
| do_test '\033]11;grey74\007' '#bdbdbd' || exit 1 | ||||
| do_test '\033]11;grey75\007' '#bfbfbf' || exit 1 | ||||
| do_test '\033]11;grey76\007' '#c2c2c2' || exit 1 | ||||
| do_test '\033]11;grey77\007' '#c4c4c4' || exit 1 | ||||
| do_test '\033]11;grey78\007' '#c7c7c7' || exit 1 | ||||
| do_test '\033]11;grey79\007' '#c9c9c9' || exit 1 | ||||
| do_test '\033]11;grey80\007' '#cccccc' || exit 1 | ||||
| do_test '\033]11;grey81\007' '#cfcfcf' || exit 1 | ||||
| do_test '\033]11;grey82\007' '#d1d1d1' || exit 1 | ||||
| do_test '\033]11;grey83\007' '#d4d4d4' || exit 1 | ||||
| do_test '\033]11;grey84\007' '#d6d6d6' || exit 1 | ||||
| do_test '\033]11;grey85\007' '#d9d9d9' || exit 1 | ||||
| do_test '\033]11;grey86\007' '#dbdbdb' || exit 1 | ||||
| do_test '\033]11;grey87\007' '#dedede' || exit 1 | ||||
| do_test '\033]11;grey88\007' '#e0e0e0' || exit 1 | ||||
| do_test '\033]11;grey89\007' '#e3e3e3' || exit 1 | ||||
| do_test '\033]11;grey90\007' '#e5e5e5' || exit 1 | ||||
| do_test '\033]11;grey91\007' '#e8e8e8' || exit 1 | ||||
| do_test '\033]11;grey92\007' '#ebebeb' || exit 1 | ||||
| do_test '\033]11;grey93\007' '#ededed' || exit 1 | ||||
| do_test '\033]11;grey94\007' '#f0f0f0' || exit 1 | ||||
| do_test '\033]11;grey95\007' '#f2f2f2' || exit 1 | ||||
| do_test '\033]11;grey96\007' '#f5f5f5' || exit 1 | ||||
| do_test '\033]11;grey97\007' '#f7f7f7' || exit 1 | ||||
| do_test '\033]11;grey98\007' '#fafafa' || exit 1 | ||||
| do_test '\033]11;grey99\007' '#fcfcfc' || exit 1 | ||||
| do_test '\033]11;grey100\007' '#ffffff' || exit 1 | ||||
|  | ||||
| do_test '\033]11;gray\007' '#bebebe' || exit 1 | ||||
| do_test '\033]11;gray0\007' '#000000' || exit 1 | ||||
| do_test '\033]11;gray1\007' '#030303' || exit 1 | ||||
| do_test '\033]11;gray2\007' '#050505' || exit 1 | ||||
| do_test '\033]11;gray3\007' '#080808' || exit 1 | ||||
| do_test '\033]11;gray4\007' '#0a0a0a' || exit 1 | ||||
| do_test '\033]11;gray5\007' '#0d0d0d' || exit 1 | ||||
| do_test '\033]11;gray6\007' '#0f0f0f' || exit 1 | ||||
| do_test '\033]11;gray7\007' '#121212' || exit 1 | ||||
| do_test '\033]11;gray8\007' '#141414' || exit 1 | ||||
| do_test '\033]11;gray9\007' '#171717' || exit 1 | ||||
| do_test '\033]11;gray10\007' '#1a1a1a' || exit 1 | ||||
| do_test '\033]11;gray11\007' '#1c1c1c' || exit 1 | ||||
| do_test '\033]11;gray12\007' '#1f1f1f' || exit 1 | ||||
| do_test '\033]11;gray13\007' '#212121' || exit 1 | ||||
| do_test '\033]11;gray14\007' '#242424' || exit 1 | ||||
| do_test '\033]11;gray15\007' '#262626' || exit 1 | ||||
| do_test '\033]11;gray16\007' '#292929' || exit 1 | ||||
| do_test '\033]11;gray17\007' '#2b2b2b' || exit 1 | ||||
| do_test '\033]11;gray18\007' '#2e2e2e' || exit 1 | ||||
| do_test '\033]11;gray19\007' '#303030' || exit 1 | ||||
| do_test '\033]11;gray20\007' '#333333' || exit 1 | ||||
| do_test '\033]11;gray21\007' '#363636' || exit 1 | ||||
| do_test '\033]11;gray22\007' '#383838' || exit 1 | ||||
| do_test '\033]11;gray23\007' '#3b3b3b' || exit 1 | ||||
| do_test '\033]11;gray24\007' '#3d3d3d' || exit 1 | ||||
| do_test '\033]11;gray25\007' '#404040' || exit 1 | ||||
| do_test '\033]11;gray26\007' '#424242' || exit 1 | ||||
| do_test '\033]11;gray27\007' '#454545' || exit 1 | ||||
| do_test '\033]11;gray28\007' '#474747' || exit 1 | ||||
| do_test '\033]11;gray29\007' '#4a4a4a' || exit 1 | ||||
| do_test '\033]11;gray30\007' '#4d4d4d' || exit 1 | ||||
| do_test '\033]11;gray31\007' '#4f4f4f' || exit 1 | ||||
| do_test '\033]11;gray32\007' '#525252' || exit 1 | ||||
| do_test '\033]11;gray33\007' '#545454' || exit 1 | ||||
| do_test '\033]11;gray34\007' '#575757' || exit 1 | ||||
| do_test '\033]11;gray35\007' '#595959' || exit 1 | ||||
| do_test '\033]11;gray36\007' '#5c5c5c' || exit 1 | ||||
| do_test '\033]11;gray37\007' '#5e5e5e' || exit 1 | ||||
| do_test '\033]11;gray38\007' '#616161' || exit 1 | ||||
| do_test '\033]11;gray39\007' '#636363' || exit 1 | ||||
| do_test '\033]11;gray40\007' '#666666' || exit 1 | ||||
| do_test '\033]11;gray41\007' '#696969' || exit 1 | ||||
| do_test '\033]11;gray42\007' '#6b6b6b' || exit 1 | ||||
| do_test '\033]11;gray43\007' '#6e6e6e' || exit 1 | ||||
| do_test '\033]11;gray44\007' '#707070' || exit 1 | ||||
| do_test '\033]11;gray45\007' '#737373' || exit 1 | ||||
| do_test '\033]11;gray46\007' '#757575' || exit 1 | ||||
| do_test '\033]11;gray47\007' '#787878' || exit 1 | ||||
| do_test '\033]11;gray48\007' '#7a7a7a' || exit 1 | ||||
| do_test '\033]11;gray49\007' '#7d7d7d' || exit 1 | ||||
| do_test '\033]11;gray50\007' '#7f7f7f' || exit 1 | ||||
| do_test '\033]11;gray51\007' '#828282' || exit 1 | ||||
| do_test '\033]11;gray52\007' '#858585' || exit 1 | ||||
| do_test '\033]11;gray53\007' '#878787' || exit 1 | ||||
| do_test '\033]11;gray54\007' '#8a8a8a' || exit 1 | ||||
| do_test '\033]11;gray55\007' '#8c8c8c' || exit 1 | ||||
| do_test '\033]11;gray56\007' '#8f8f8f' || exit 1 | ||||
| do_test '\033]11;gray57\007' '#919191' || exit 1 | ||||
| do_test '\033]11;gray58\007' '#949494' || exit 1 | ||||
| do_test '\033]11;gray59\007' '#969696' || exit 1 | ||||
| do_test '\033]11;gray60\007' '#999999' || exit 1 | ||||
| do_test '\033]11;gray61\007' '#9c9c9c' || exit 1 | ||||
| do_test '\033]11;gray62\007' '#9e9e9e' || exit 1 | ||||
| do_test '\033]11;gray63\007' '#a1a1a1' || exit 1 | ||||
| do_test '\033]11;gray64\007' '#a3a3a3' || exit 1 | ||||
| do_test '\033]11;gray65\007' '#a6a6a6' || exit 1 | ||||
| do_test '\033]11;gray66\007' '#a8a8a8' || exit 1 | ||||
| do_test '\033]11;gray67\007' '#ababab' || exit 1 | ||||
| do_test '\033]11;gray68\007' '#adadad' || exit 1 | ||||
| do_test '\033]11;gray69\007' '#b0b0b0' || exit 1 | ||||
| do_test '\033]11;gray70\007' '#b3b3b3' || exit 1 | ||||
| do_test '\033]11;gray71\007' '#b5b5b5' || exit 1 | ||||
| do_test '\033]11;gray72\007' '#b8b8b8' || exit 1 | ||||
| do_test '\033]11;gray73\007' '#bababa' || exit 1 | ||||
| do_test '\033]11;gray74\007' '#bdbdbd' || exit 1 | ||||
| do_test '\033]11;gray75\007' '#bfbfbf' || exit 1 | ||||
| do_test '\033]11;gray76\007' '#c2c2c2' || exit 1 | ||||
| do_test '\033]11;gray77\007' '#c4c4c4' || exit 1 | ||||
| do_test '\033]11;gray78\007' '#c7c7c7' || exit 1 | ||||
| do_test '\033]11;gray79\007' '#c9c9c9' || exit 1 | ||||
| do_test '\033]11;gray80\007' '#cccccc' || exit 1 | ||||
| do_test '\033]11;gray81\007' '#cfcfcf' || exit 1 | ||||
| do_test '\033]11;gray82\007' '#d1d1d1' || exit 1 | ||||
| do_test '\033]11;gray83\007' '#d4d4d4' || exit 1 | ||||
| do_test '\033]11;gray84\007' '#d6d6d6' || exit 1 | ||||
| do_test '\033]11;gray85\007' '#d9d9d9' || exit 1 | ||||
| do_test '\033]11;gray86\007' '#dbdbdb' || exit 1 | ||||
| do_test '\033]11;gray87\007' '#dedede' || exit 1 | ||||
| do_test '\033]11;gray88\007' '#e0e0e0' || exit 1 | ||||
| do_test '\033]11;gray89\007' '#e3e3e3' || exit 1 | ||||
| do_test '\033]11;gray90\007' '#e5e5e5' || exit 1 | ||||
| do_test '\033]11;gray91\007' '#e8e8e8' || exit 1 | ||||
| do_test '\033]11;gray92\007' '#ebebeb' || exit 1 | ||||
| do_test '\033]11;gray93\007' '#ededed' || exit 1 | ||||
| do_test '\033]11;gray94\007' '#f0f0f0' || exit 1 | ||||
| do_test '\033]11;gray95\007' '#f2f2f2' || exit 1 | ||||
| do_test '\033]11;gray96\007' '#f5f5f5' || exit 1 | ||||
| do_test '\033]11;gray97\007' '#f7f7f7' || exit 1 | ||||
| do_test '\033]11;gray98\007' '#fafafa' || exit 1 | ||||
| do_test '\033]11;gray99\007' '#fcfcfc' || exit 1 | ||||
| do_test '\033]11;gray100\007' '#ffffff' || exit 1 | ||||
|  | ||||
| $TMUX -f/dev/null kill-server 2>/dev/null | ||||
| exit 0 | ||||
| @@ -32,8 +32,8 @@ static void	screen_redraw_set_context(struct client *, | ||||
| 		    struct screen_redraw_ctx *); | ||||
|  | ||||
| #define CELL_INSIDE 0 | ||||
| #define CELL_LEFTRIGHT 1 | ||||
| #define CELL_TOPBOTTOM 2 | ||||
| #define CELL_TOPBOTTOM 1 | ||||
| #define CELL_LEFTRIGHT 2 | ||||
| #define CELL_TOPLEFT 3 | ||||
| #define CELL_TOPRIGHT 4 | ||||
| #define CELL_BOTTOMLEFT 5 | ||||
| @@ -47,6 +47,9 @@ static void	screen_redraw_set_context(struct client *, | ||||
|  | ||||
| #define CELL_BORDERS " xqlkmjwvtun~" | ||||
|  | ||||
| #define START_ISOLATE "\342\201\246" | ||||
| #define END_ISOLATE   "\342\201\251" | ||||
|  | ||||
| static const struct utf8_data screen_redraw_double_borders[] = { | ||||
| 	{ "", 0, 0, 0 }, | ||||
| 	{ "\342\225\221", 0, 3, 1 }, /* U+2551 */ | ||||
| @@ -299,7 +302,7 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, | ||||
| 	case 13:	/* 1101, left right bottom */ | ||||
| 		return (CELL_TOPJOIN); | ||||
| 	case 12:	/* 1100, left right */ | ||||
| 		return (CELL_TOPBOTTOM); | ||||
| 		return (CELL_LEFTRIGHT); | ||||
| 	case 11:	/* 1011, left top bottom */ | ||||
| 		return (CELL_RIGHTJOIN); | ||||
| 	case 10:	/* 1010, left top */ | ||||
| @@ -313,7 +316,7 @@ screen_redraw_type_of_cell(struct client *c, u_int px, u_int py, | ||||
| 	case 5:		/* 0101, right bottom */ | ||||
| 		return (CELL_TOPLEFT); | ||||
| 	case 3:		/* 0011, top bottom */ | ||||
| 		return (CELL_LEFTRIGHT); | ||||
| 		return (CELL_TOPBOTTOM); | ||||
| 	} | ||||
| 	return (CELL_OUTSIDE); | ||||
| } | ||||
| @@ -407,7 +410,7 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, | ||||
| 	struct format_tree	*ft; | ||||
| 	char			*expanded; | ||||
| 	int			 pane_status = rctx->pane_status; | ||||
| 	u_int			 width, i, cell_type, top, px, py; | ||||
| 	u_int			 width, i, cell_type, px, py; | ||||
| 	struct screen_write_ctx	 ctx; | ||||
| 	struct screen		 old; | ||||
|  | ||||
| @@ -432,16 +435,12 @@ screen_redraw_make_pane_status(struct client *c, struct window_pane *wp, | ||||
|  | ||||
| 	screen_write_start(&ctx, &wp->status_screen); | ||||
|  | ||||
| 	if (rctx->statustop) | ||||
| 		top = rctx->statuslines; | ||||
| 	else | ||||
| 		top = 0; | ||||
| 	for (i = 0; i < width; i++) { | ||||
| 		px = wp->xoff + 2 + i; | ||||
| 		if (rctx->pane_status == PANE_STATUS_TOP) | ||||
| 			py = top + wp->yoff - 1; | ||||
| 			py = wp->yoff - 1; | ||||
| 		else | ||||
| 			py = top + wp->yoff + wp->sy; | ||||
| 			py = wp->yoff + wp->sy; | ||||
| 		cell_type = screen_redraw_type_of_cell(c, px, py, pane_status); | ||||
| 		screen_redraw_border_set(wp, pane_lines, cell_type, &gc); | ||||
| 		screen_write_cell(&ctx, &gc); | ||||
| @@ -684,7 +683,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) | ||||
| 	struct tty		*tty = &c->tty; | ||||
| 	struct window_pane	*wp; | ||||
| 	u_int			 cell_type, x = ctx->ox + i, y = ctx->oy + j; | ||||
| 	int			 pane_status = ctx->pane_status; | ||||
| 	int			 pane_status = ctx->pane_status, isolates; | ||||
| 	struct grid_cell	 gc; | ||||
| 	const struct grid_cell	*tmp; | ||||
|  | ||||
| @@ -709,11 +708,22 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) | ||||
| 	} | ||||
| 	screen_redraw_border_set(wp, ctx->pane_lines, cell_type, &gc); | ||||
|  | ||||
| 	if (cell_type == CELL_TOPBOTTOM && | ||||
| 	    (c->flags & CLIENT_UTF8) && | ||||
| 	    tty_term_has(tty->term, TTYC_BIDI)) | ||||
| 		isolates = 1; | ||||
| 	else | ||||
| 		isolates = 0; | ||||
|  | ||||
| 	if (ctx->statustop) | ||||
| 		tty_cursor(tty, i, ctx->statuslines + j); | ||||
| 	else | ||||
| 		tty_cursor(tty, i, j); | ||||
| 	if (isolates) | ||||
| 		tty_puts(tty, END_ISOLATE); | ||||
| 	tty_cell(tty, &gc, &grid_default_cell, NULL); | ||||
| 	if (isolates) | ||||
| 		tty_puts(tty, START_ISOLATE); | ||||
| } | ||||
|  | ||||
| /* Draw the borders. */ | ||||
|   | ||||
							
								
								
									
										377
									
								
								screen-write.c
									
									
									
									
									
								
							
							
						
						
									
										377
									
								
								screen-write.c
									
									
									
									
									
								
							| @@ -23,13 +23,11 @@ | ||||
|  | ||||
| #include "tmux.h" | ||||
|  | ||||
| static struct screen_write_citem *screen_write_collect_trim( | ||||
| 		    struct screen_write_ctx *, u_int, u_int, u_int, int *); | ||||
| static void	screen_write_collect_clear(struct screen_write_ctx *, u_int, | ||||
| 		    u_int); | ||||
| static void	screen_write_collect_clear_end(struct screen_write_ctx *, u_int, | ||||
| 		    u_int); | ||||
| static void	screen_write_collect_clear_start(struct screen_write_ctx *, | ||||
| 		    u_int, u_int); | ||||
| static void	screen_write_collect_scroll(struct screen_write_ctx *); | ||||
| static void	screen_write_collect_scroll(struct screen_write_ctx *, u_int); | ||||
| static void	screen_write_collect_flush(struct screen_write_ctx *, int, | ||||
| 		    const char *); | ||||
|  | ||||
| @@ -38,23 +36,44 @@ static int	screen_write_overwrite(struct screen_write_ctx *, | ||||
| static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, | ||||
| 		    const struct utf8_data *, u_int *); | ||||
|  | ||||
| struct screen_write_collect_item { | ||||
| 	u_int			 x; | ||||
| 	int			 wrapped; | ||||
| struct screen_write_citem { | ||||
| 	u_int				x; | ||||
| 	int				wrapped; | ||||
|  | ||||
| 	enum { TEXT, CLEAR_END, CLEAR_START } type; | ||||
| 	u_int			 used; | ||||
| 	u_int			 bg; | ||||
| 	enum { TEXT, CLEAR }		type; | ||||
| 	u_int				used; | ||||
| 	u_int				bg; | ||||
|  | ||||
| 	struct grid_cell	 gc; | ||||
| 	struct grid_cell		gc; | ||||
|  | ||||
| 	TAILQ_ENTRY(screen_write_collect_item) entry; | ||||
| 	TAILQ_ENTRY(screen_write_citem) entry; | ||||
| }; | ||||
| struct screen_write_collect_line { | ||||
| 	u_int					 bg; | ||||
| 	char					*data; | ||||
| 	TAILQ_HEAD(, screen_write_collect_item)  items; | ||||
| struct screen_write_cline { | ||||
| 	char				*data; | ||||
| 	TAILQ_HEAD(, screen_write_citem) items; | ||||
| }; | ||||
| TAILQ_HEAD(, screen_write_citem)  screen_write_citem_freelist = | ||||
|     TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist); | ||||
|  | ||||
| static struct screen_write_citem * | ||||
| screen_write_get_citem(void) | ||||
| { | ||||
|     struct screen_write_citem	*ci; | ||||
|  | ||||
|     ci = TAILQ_FIRST(&screen_write_citem_freelist); | ||||
|     if (ci != NULL) { | ||||
|         TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry); | ||||
|         memset(ci, 0, sizeof *ci); | ||||
|         return (ci); | ||||
|     } | ||||
|     return (xcalloc(1, sizeof *ci)); | ||||
| } | ||||
|  | ||||
| static void | ||||
| screen_write_free_citem(struct screen_write_citem *ci) | ||||
| { | ||||
|     TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry); | ||||
| } | ||||
|  | ||||
| static void | ||||
| screen_write_offset_timer(__unused int fd, __unused short events, void *data) | ||||
| @@ -125,7 +144,8 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c) | ||||
| 		 * Redraw is already deferred to redraw another pane - redraw | ||||
| 		 * this one also when that happens. | ||||
| 		 */ | ||||
| 		log_debug("adding %%%u to deferred redraw", wp->id); | ||||
| 		log_debug("%s: adding %%%u to deferred redraw", __func__, | ||||
| 		    wp->id); | ||||
| 		wp->flags |= PANE_REDRAW; | ||||
| 		return (-1); | ||||
| 	} | ||||
| @@ -220,7 +240,7 @@ screen_write_init(struct screen_write_ctx *ctx, struct screen *s) | ||||
|  | ||||
| 	if (ctx->s->write_list == NULL) | ||||
| 		screen_write_make_list(ctx->s); | ||||
| 	ctx->item = xcalloc(1, sizeof *ctx->item); | ||||
| 	ctx->item = screen_write_get_citem(); | ||||
|  | ||||
| 	ctx->scrolled = 0; | ||||
| 	ctx->bg = 8; | ||||
| @@ -259,7 +279,6 @@ screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s, | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Initialize writing. */ | ||||
| void | ||||
| screen_write_start(struct screen_write_ctx *ctx, struct screen *s) | ||||
| @@ -279,14 +298,7 @@ screen_write_stop(struct screen_write_ctx *ctx) | ||||
| 	screen_write_collect_end(ctx); | ||||
| 	screen_write_collect_flush(ctx, 0, __func__); | ||||
|  | ||||
| 	log_debug("%s: %u cells (%u written, %u skipped)", __func__, | ||||
| 	    ctx->cells, ctx->written, ctx->skipped); | ||||
| 	if (ctx->wp != NULL) { | ||||
| 		ctx->wp->written += ctx->written; | ||||
| 		ctx->wp->skipped += ctx->skipped; | ||||
| 	} | ||||
|  | ||||
| 	free(ctx->item); | ||||
| 	screen_write_free_citem(ctx->item); | ||||
| } | ||||
|  | ||||
| /* Reset screen state. */ | ||||
| @@ -1095,9 +1107,10 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) | ||||
| void | ||||
| screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) | ||||
| { | ||||
| 	struct screen		 *s = ctx->s; | ||||
| 	struct grid_line	 *gl; | ||||
| 	u_int			  sx = screen_size_x(s); | ||||
| 	struct screen			*s = ctx->s; | ||||
| 	struct grid_line		*gl; | ||||
| 	u_int				 sx = screen_size_x(s); | ||||
| 	struct screen_write_citem	*ci = ctx->item; | ||||
|  | ||||
| 	gl = grid_get_line(s->grid, s->grid->hsize + s->cy); | ||||
| 	if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) | ||||
| @@ -1106,18 +1119,22 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) | ||||
| 	grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); | ||||
|  | ||||
| 	screen_write_collect_clear(ctx, s->cy, 1); | ||||
| 	ctx->s->write_list[s->cy].bg = 1 + bg; | ||||
| 	ctx->item->used = 0; | ||||
| 	ci->x = 0; | ||||
| 	ci->used = sx; | ||||
| 	ci->type = CLEAR; | ||||
| 	ci->bg = bg; | ||||
| 	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); | ||||
| 	ctx->item = screen_write_get_citem(); | ||||
| } | ||||
|  | ||||
| /* Clear to end of line from cursor. */ | ||||
| void | ||||
| screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) | ||||
| { | ||||
| 	struct screen			 *s = ctx->s; | ||||
| 	struct grid_line		 *gl; | ||||
| 	u_int				  sx = screen_size_x(s); | ||||
| 	struct screen_write_collect_item *ci = ctx->item; | ||||
| 	struct screen			*s = ctx->s; | ||||
| 	struct grid_line		*gl; | ||||
| 	u_int				 sx = screen_size_x(s); | ||||
| 	struct screen_write_citem	*ci = ctx->item, *before; | ||||
|  | ||||
| 	if (s->cx == 0) { | ||||
| 		screen_write_clearline(ctx, bg); | ||||
| @@ -1130,12 +1147,16 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) | ||||
|  | ||||
| 	grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); | ||||
|  | ||||
|  	screen_write_collect_clear_end(ctx, s->cy, s->cx); | ||||
|  	before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL); | ||||
| 	ci->x = s->cx; | ||||
| 	ci->type = CLEAR_END; | ||||
| 	ci->used = sx - s->cx; | ||||
| 	ci->type = CLEAR; | ||||
| 	ci->bg = bg; | ||||
| 	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); | ||||
| 	ctx->item = xcalloc(1, sizeof *ctx->item); | ||||
| 	if (before == NULL) | ||||
| 		TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); | ||||
| 	else | ||||
| 		TAILQ_INSERT_BEFORE(before, ci, entry); | ||||
| 	ctx->item = screen_write_get_citem(); | ||||
| } | ||||
|  | ||||
| /* Clear to start of line from cursor. */ | ||||
| @@ -1143,8 +1164,8 @@ void | ||||
| screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) | ||||
| { | ||||
| 	struct screen			 *s = ctx->s; | ||||
| 	u_int				  sx = screen_size_x(s); | ||||
| 	struct screen_write_collect_item *ci = ctx->item; | ||||
| 	u_int				 sx = screen_size_x(s); | ||||
| 	struct screen_write_citem	*ci = ctx->item, *before; | ||||
|  | ||||
| 	if (s->cx >= sx - 1) { | ||||
| 		screen_write_clearline(ctx, bg); | ||||
| @@ -1156,12 +1177,16 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) | ||||
| 	else | ||||
| 		grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); | ||||
|  | ||||
| 	screen_write_collect_clear_start(ctx, s->cy, s->cx); | ||||
| 	ci->x = s->cx; | ||||
| 	ci->type = CLEAR_START; | ||||
| 	before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL); | ||||
| 	ci->x = 0; | ||||
| 	ci->used = s->cx + 1; | ||||
| 	ci->type = CLEAR; | ||||
| 	ci->bg = bg; | ||||
| 	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); | ||||
| 	ctx->item = xcalloc(1, sizeof *ctx->item); | ||||
| 	if (before == NULL) | ||||
| 		TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); | ||||
| 	else | ||||
| 		TAILQ_INSERT_BEFORE(before, ci, entry); | ||||
| 	ctx->item = screen_write_get_citem(); | ||||
| } | ||||
|  | ||||
| /* Move cursor to px,py. */ | ||||
| @@ -1183,6 +1208,7 @@ screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py, | ||||
| 	if (py != -1 && (u_int)py > screen_size_y(s) - 1) | ||||
| 		py = screen_size_y(s) - 1; | ||||
|  | ||||
| 	log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py); | ||||
| 	screen_write_set_cursor(ctx, px, py); | ||||
| } | ||||
|  | ||||
| @@ -1240,8 +1266,6 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) | ||||
| 	gl = grid_get_line(gd, gd->hsize + s->cy); | ||||
| 	if (wrapped) | ||||
| 		gl->flags |= GRID_LINE_WRAPPED; | ||||
| 	else | ||||
| 		gl->flags &= ~GRID_LINE_WRAPPED; | ||||
|  | ||||
| 	log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, | ||||
| 	    s->rupper, s->rlower); | ||||
| @@ -1253,7 +1277,7 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) | ||||
|  | ||||
| 	if (s->cy == s->rlower) { | ||||
| 		grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); | ||||
| 		screen_write_collect_scroll(ctx); | ||||
| 		screen_write_collect_scroll(ctx, bg); | ||||
| 		ctx->scrolled++; | ||||
| 	} else if (s->cy < screen_size_y(s) - 1) | ||||
| 		screen_write_set_cursor(ctx, -1, s->cy + 1); | ||||
| @@ -1279,7 +1303,7 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) | ||||
|  | ||||
| 	for (i = 0; i < lines; i++) { | ||||
| 		grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); | ||||
| 		screen_write_collect_scroll(ctx); | ||||
| 		screen_write_collect_scroll(ctx, bg); | ||||
| 	} | ||||
| 	ctx->scrolled += lines; | ||||
| } | ||||
| @@ -1393,107 +1417,114 @@ screen_write_clearhistory(struct screen_write_ctx *ctx) | ||||
| 	grid_clear_history(ctx->s->grid); | ||||
| } | ||||
|  | ||||
| /* Clear to start of a collected line. */ | ||||
| static void | ||||
| screen_write_collect_clear_start(struct screen_write_ctx *ctx, u_int y, u_int x) | ||||
| /* Trim collected items. */ | ||||
| static struct screen_write_citem * | ||||
| screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x, | ||||
|     u_int used, int *wrapped) | ||||
| { | ||||
| 	struct screen_write_collect_item	*ci, *tmp; | ||||
| 	size_t					 size = 0; | ||||
| 	u_int					 items = 0; | ||||
| 	struct screen_write_cline	*cl = &ctx->s->write_list[y]; | ||||
| 	struct screen_write_citem	*ci, *ci2, *tmp, *before = NULL; | ||||
| 	u_int				 sx = x, ex = x + used - 1; | ||||
| 	u_int				 csx, cex; | ||||
|  | ||||
| 	if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) | ||||
| 		return; | ||||
| 	TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { | ||||
| 		switch (ci->type) { | ||||
| 		case CLEAR_START: | ||||
| 			break; | ||||
| 		case CLEAR_END: | ||||
| 			if (ci->x <= x) | ||||
| 				ci->x = x; | ||||
| 	if (TAILQ_EMPTY(&cl->items)) | ||||
| 		return (NULL); | ||||
| 	TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { | ||||
| 		csx = ci->x; | ||||
| 		cex = ci->x + ci->used - 1; | ||||
|  | ||||
| 		/* Item is entirely before. */ | ||||
| 		if (cex < sx) { | ||||
| 			log_debug("%s: %p %u-%u before %u-%u", __func__, ci, | ||||
| 			    csx, cex, sx, ex); | ||||
| 			continue; | ||||
| 		case TEXT: | ||||
| 			if (ci->x > x) | ||||
| 				continue; | ||||
| 		} | ||||
|  | ||||
| 		/* Item is entirely after. */ | ||||
| 		if (csx > ex) { | ||||
| 			log_debug("%s: %p %u-%u after %u-%u", __func__, ci, | ||||
| 			    csx, cex, sx, ex); | ||||
| 			before = ci; | ||||
| 			break; | ||||
| 		} | ||||
| 		items++; | ||||
| 		size += ci->used; | ||||
| 		TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry); | ||||
| 		free(ci); | ||||
| 	} | ||||
| 	ctx->skipped += size; | ||||
| 	log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, | ||||
| 	    size, y); | ||||
| } | ||||
|  | ||||
| /* Clear to end of a collected line. */ | ||||
| static void | ||||
| screen_write_collect_clear_end(struct screen_write_ctx *ctx, u_int y, u_int x) | ||||
| { | ||||
| 	struct screen_write_collect_item	*ci, *tmp; | ||||
| 	size_t					 size = 0; | ||||
| 	u_int					 items = 0; | ||||
|  | ||||
| 	if (TAILQ_EMPTY(&ctx->s->write_list[y].items)) | ||||
| 		return; | ||||
| 	TAILQ_FOREACH_SAFE(ci, &ctx->s->write_list[y].items, entry, tmp) { | ||||
| 		switch (ci->type) { | ||||
| 		case CLEAR_START: | ||||
| 			if (ci->x >= x) | ||||
| 				ci->x = x; | ||||
| 		/* Item is entirely inside. */ | ||||
| 		if (csx >= sx && cex <= ex) { | ||||
| 			log_debug("%s: %p %u-%u inside %u-%u", __func__, ci, | ||||
| 			    csx, cex, sx, ex); | ||||
| 			TAILQ_REMOVE(&cl->items, ci, entry); | ||||
| 			screen_write_free_citem(ci); | ||||
| 			if (csx == 0 && ci->wrapped && wrapped != NULL) | ||||
| 				*wrapped = 1; | ||||
| 			continue; | ||||
| 		case CLEAR_END: | ||||
| 			break; | ||||
| 		case TEXT: | ||||
| 			if (ci->x < x) | ||||
| 				continue; | ||||
| 		} | ||||
|  | ||||
| 		/* Item under the start. */ | ||||
| 		if (csx < sx && cex >= sx && cex <= ex) { | ||||
| 			log_debug("%s: %p %u-%u start %u-%u", __func__, ci, | ||||
| 			    csx, cex, sx, ex); | ||||
| 			ci->used = sx - csx; | ||||
| 			log_debug("%s: %p now %u-%u", __func__, ci, ci->x, | ||||
| 			    ci->x + ci->used + 1); | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		/* Item covers the end. */ | ||||
| 		if (cex > ex && csx >= sx && csx <= ex) { | ||||
| 			log_debug("%s: %p %u-%u end %u-%u", __func__, ci, | ||||
| 			    csx, cex, sx, ex); | ||||
| 			ci->x = ex + 1; | ||||
| 			ci->used = cex - ex; | ||||
| 			log_debug("%s: %p now %u-%u", __func__, ci, ci->x, | ||||
| 			    ci->x + ci->used + 1); | ||||
| 			before = ci; | ||||
| 			break; | ||||
| 		} | ||||
| 		items++; | ||||
| 		size += ci->used; | ||||
| 		TAILQ_REMOVE(&ctx->s->write_list[y].items, ci, entry); | ||||
| 		free(ci); | ||||
|  | ||||
| 		/* Item must cover both sides. */ | ||||
| 		log_debug("%s: %p %u-%u under %u-%u", __func__, ci, | ||||
| 		    csx, cex, sx, ex); | ||||
| 		ci2 = screen_write_get_citem(); | ||||
| 		ci2->type = ci->type; | ||||
| 		ci2->bg = ci->bg; | ||||
| 		memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc); | ||||
| 		TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry); | ||||
|  | ||||
| 		ci->used = sx - csx; | ||||
| 		ci2->x = ex + 1; | ||||
| 		ci2->used = cex - ex; | ||||
|  | ||||
| 		log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci, | ||||
| 		    ci->x, ci->x + ci->used - 1, ci, ci2->x, | ||||
| 		    ci2->x + ci2->used - 1, ci2); | ||||
| 		before = ci2; | ||||
| 		break; | ||||
| 	} | ||||
| 	ctx->skipped += size; | ||||
| 	log_debug("%s: dropped %u items (%zu bytes) (line %u)", __func__, items, | ||||
| 	    size, y); | ||||
| 	return (before); | ||||
| } | ||||
|  | ||||
| /* Clear collected lines. */ | ||||
| static void | ||||
| screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) | ||||
| { | ||||
| 	struct screen_write_collect_item	*ci, *tmp; | ||||
| 	struct screen_write_collect_line	*cl; | ||||
| 	u_int					 i, items; | ||||
| 	size_t					 size; | ||||
| 	struct screen_write_cline	*cl; | ||||
| 	u_int				 i; | ||||
|  | ||||
| 	for (i = y; i < y + n; i++) { | ||||
| 		if (TAILQ_EMPTY(&ctx->s->write_list[i].items)) | ||||
| 			continue; | ||||
| 		items = 0; | ||||
| 		size = 0; | ||||
| 		cl = &ctx->s->write_list[i]; | ||||
| 		TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { | ||||
| 			items++; | ||||
| 			size += ci->used; | ||||
| 			TAILQ_REMOVE(&cl->items, ci, entry); | ||||
| 			free(ci); | ||||
| 		} | ||||
| 		ctx->skipped += size; | ||||
| 		log_debug("%s: dropped %u items (%zu bytes) (line %u)", | ||||
| 		    __func__, items, size, y); | ||||
| 		TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Scroll collected lines up. */ | ||||
| static void | ||||
| screen_write_collect_scroll(struct screen_write_ctx *ctx) | ||||
| screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg) | ||||
| { | ||||
| 	struct screen				*s = ctx->s; | ||||
| 	struct screen_write_collect_line	*cl; | ||||
| 	u_int					 y; | ||||
| 	char					*saved; | ||||
| 	struct screen			*s = ctx->s; | ||||
| 	struct screen_write_cline	*cl; | ||||
| 	u_int				 y; | ||||
| 	char				*saved; | ||||
| 	struct screen_write_citem	*ci; | ||||
|  | ||||
| 	log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, | ||||
| 	    s->rupper, s->rlower); | ||||
| @@ -1503,11 +1534,16 @@ screen_write_collect_scroll(struct screen_write_ctx *ctx) | ||||
| 	for (y = s->rupper; y < s->rlower; y++) { | ||||
| 		cl = &ctx->s->write_list[y + 1]; | ||||
| 		TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry); | ||||
| 		ctx->s->write_list[y].bg = cl->bg; | ||||
| 		ctx->s->write_list[y].data = cl->data; | ||||
| 	} | ||||
| 	ctx->s->write_list[s->rlower].bg = 1 + 8; | ||||
| 	ctx->s->write_list[s->rlower].data = saved; | ||||
|  | ||||
| 	ci = screen_write_get_citem(); | ||||
| 	ci->x = 0; | ||||
| 	ci->used = screen_size_x(s); | ||||
| 	ci->type = CLEAR; | ||||
| 	ci->bg = bg; | ||||
| 	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry); | ||||
| } | ||||
|  | ||||
| /* Flush collected lines. */ | ||||
| @@ -1515,12 +1551,11 @@ static void | ||||
| screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, | ||||
|     const char *from) | ||||
| { | ||||
| 	struct screen				*s = ctx->s; | ||||
| 	struct screen_write_collect_item	*ci, *tmp; | ||||
| 	struct screen_write_collect_line	*cl; | ||||
| 	u_int					 y, cx, cy, items = 0; | ||||
| 	struct tty_ctx				 ttyctx; | ||||
| 	size_t					 written = 0; | ||||
| 	struct screen			*s = ctx->s; | ||||
| 	struct screen_write_citem	*ci, *tmp; | ||||
| 	struct screen_write_cline	*cl; | ||||
| 	u_int				 y, cx, cy, last, items = 0; | ||||
| 	struct tty_ctx			 ttyctx; | ||||
|  | ||||
| 	if (ctx->scrolled != 0) { | ||||
| 		log_debug("%s: scrolled %u (region %u-%u)", __func__, | ||||
| @@ -1542,23 +1577,18 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, | ||||
| 	cx = s->cx; cy = s->cy; | ||||
| 	for (y = 0; y < screen_size_y(s); y++) { | ||||
| 		cl = &ctx->s->write_list[y]; | ||||
| 		if (cl->bg != 0) { | ||||
| 			screen_write_set_cursor(ctx, 0, y); | ||||
| 			screen_write_initctx(ctx, &ttyctx, 1); | ||||
| 			ttyctx.bg = cl->bg - 1; | ||||
| 			tty_write(tty_cmd_clearline, &ttyctx); | ||||
| 		} | ||||
| 		last = UINT_MAX; | ||||
| 		TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { | ||||
| 			if (last != UINT_MAX && ci->x <= last) { | ||||
| 				fatalx("collect list not in order: %u <= %u", | ||||
| 				    ci->x, last); | ||||
| 			} | ||||
| 			screen_write_set_cursor(ctx, ci->x, y); | ||||
| 			if (ci->type == CLEAR_END) { | ||||
| 				log_debug("XXX %u %u", ci->x, ci->bg); | ||||
| 			if (ci->type == CLEAR) { | ||||
| 				screen_write_initctx(ctx, &ttyctx, 1); | ||||
| 				ttyctx.bg = ci->bg; | ||||
| 				tty_write(tty_cmd_clearendofline, &ttyctx); | ||||
| 			} else if (ci->type == CLEAR_START) { | ||||
| 				screen_write_initctx(ctx, &ttyctx, 1); | ||||
| 				ttyctx.bg = ci->bg; | ||||
| 				tty_write(tty_cmd_clearstartofline, &ttyctx); | ||||
| 				ttyctx.num = ci->used; | ||||
| 				tty_write(tty_cmd_clearcharacter, &ttyctx); | ||||
| 			} else { | ||||
| 				screen_write_initctx(ctx, &ttyctx, 0); | ||||
| 				ttyctx.cell = &ci->gc; | ||||
| @@ -1567,38 +1597,41 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, | ||||
| 				ttyctx.num = ci->used; | ||||
| 				tty_write(tty_cmd_cells, &ttyctx); | ||||
| 			} | ||||
|  | ||||
| 			items++; | ||||
| 			written += ci->used; | ||||
|  | ||||
| 			TAILQ_REMOVE(&cl->items, ci, entry); | ||||
| 			free(ci); | ||||
| 			screen_write_free_citem(ci); | ||||
| 			last = ci->x; | ||||
| 		} | ||||
| 		cl->bg = 0; | ||||
| 	} | ||||
| 	s->cx = cx; s->cy = cy; | ||||
|  | ||||
| 	log_debug("%s: flushed %u items (%zu bytes) (%s)", __func__, items, | ||||
| 	    written, from); | ||||
| 	ctx->written += written; | ||||
| 	log_debug("%s: flushed %u items (%s)", __func__, items, from); | ||||
| } | ||||
|  | ||||
| /* Finish and store collected cells. */ | ||||
| void | ||||
| screen_write_collect_end(struct screen_write_ctx *ctx) | ||||
| { | ||||
| 	struct screen				*s = ctx->s; | ||||
| 	struct screen_write_collect_item	*ci = ctx->item; | ||||
| 	struct screen_write_collect_line	*cl = &s->write_list[s->cy]; | ||||
| 	struct grid_cell			 gc; | ||||
| 	u_int					 xx; | ||||
| 	struct screen			*s = ctx->s; | ||||
| 	struct screen_write_citem	*ci = ctx->item, *before; | ||||
| 	struct screen_write_cline	*cl = &s->write_list[s->cy]; | ||||
| 	struct grid_cell		 gc; | ||||
| 	u_int				 xx; | ||||
| 	int				 wrapped = ci->wrapped; | ||||
|  | ||||
| 	if (ci->used == 0) | ||||
| 		return; | ||||
|  | ||||
| 	before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used, | ||||
| 	    &wrapped); | ||||
| 	ci->x = s->cx; | ||||
| 	TAILQ_INSERT_TAIL(&cl->items, ci, entry); | ||||
| 	ctx->item = xcalloc(1, sizeof *ctx->item); | ||||
| 	ci->wrapped = wrapped; | ||||
| 	if (before == NULL) | ||||
| 		TAILQ_INSERT_TAIL(&cl->items, ci, entry); | ||||
| 	else | ||||
| 		TAILQ_INSERT_BEFORE(before, ci, entry); | ||||
| 	ctx->item = screen_write_get_citem(); | ||||
|  | ||||
| 	log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used, | ||||
| 	    (int)ci->used, cl->data + ci->x, s->cx, s->cy); | ||||
| @@ -1634,10 +1667,10 @@ void | ||||
| screen_write_collect_add(struct screen_write_ctx *ctx, | ||||
|     const struct grid_cell *gc) | ||||
| { | ||||
| 	struct screen				*s = ctx->s; | ||||
| 	struct screen_write_collect_item	*ci; | ||||
| 	u_int					 sx = screen_size_x(s); | ||||
| 	int					 collect; | ||||
| 	struct screen			*s = ctx->s; | ||||
| 	struct screen_write_citem	*ci; | ||||
| 	u_int				 sx = screen_size_x(s); | ||||
| 	int				 collect; | ||||
|  | ||||
| 	/* | ||||
| 	 * Don't need to check that the attributes and whatnot are still the | ||||
| @@ -1662,7 +1695,6 @@ screen_write_collect_add(struct screen_write_ctx *ctx, | ||||
| 		screen_write_cell(ctx, gc); | ||||
| 		return; | ||||
| 	} | ||||
| 	ctx->cells++; | ||||
|  | ||||
| 	if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx) | ||||
| 		screen_write_collect_end(ctx); | ||||
| @@ -1699,7 +1731,6 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) | ||||
| 	/* Ignore padding cells. */ | ||||
| 	if (gc->flags & GRID_FLAG_PADDING) | ||||
| 		return; | ||||
| 	ctx->cells++; | ||||
|  | ||||
| 	/* If the width is zero, combine onto the previous character. */ | ||||
| 	if (width == 0) { | ||||
| @@ -1826,9 +1857,7 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) | ||||
| 		} else | ||||
| 			ttyctx.cell = gc; | ||||
| 		tty_write(tty_cmd_cell, &ttyctx); | ||||
| 		ctx->written++; | ||||
| 	} else | ||||
| 		ctx->skipped++; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Combine a UTF-8 zero-width character onto the previous. */ | ||||
| @@ -1974,6 +2003,8 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, | ||||
|  | ||||
| 	if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) | ||||
| 		return; | ||||
|  | ||||
| 	screen_write_collect_flush(ctx, 0, __func__); | ||||
| 	screen_alternate_on(ctx->s, gc, cursor); | ||||
|  | ||||
| 	screen_write_initctx(ctx, &ttyctx, 1); | ||||
| @@ -1990,6 +2021,8 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc, | ||||
|  | ||||
| 	if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) | ||||
| 		return; | ||||
|  | ||||
| 	screen_write_collect_flush(ctx, 0, __func__); | ||||
| 	screen_alternate_off(ctx->s, gc, cursor); | ||||
|  | ||||
| 	screen_write_initctx(ctx, &ttyctx, 1); | ||||
|   | ||||
							
								
								
									
										41
									
								
								screen.c
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								screen.c
									
									
									
									
									
								
							| @@ -573,7 +573,14 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) | ||||
| void | ||||
| screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) | ||||
| { | ||||
| 	u_int	sx, sy; | ||||
| 	u_int	sx = screen_size_x(s), sy = screen_size_y(s); | ||||
|  | ||||
| 	/* | ||||
| 	 * If the current size is different, temporarily resize to the old size | ||||
| 	 * before copying back. | ||||
| 	 */ | ||||
| 	if (s->saved_grid != NULL) | ||||
| 		screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 1); | ||||
|  | ||||
| 	/* | ||||
| 	 * Restore the cursor position and cell. This happens even if not | ||||
| @@ -581,29 +588,23 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) | ||||
| 	 */ | ||||
| 	if (cursor && s->saved_cx != UINT_MAX && s->saved_cy != UINT_MAX) { | ||||
| 		s->cx = s->saved_cx; | ||||
| 		if (s->cx > screen_size_x(s) - 1) | ||||
| 			s->cx = screen_size_x(s) - 1; | ||||
| 		s->cy = s->saved_cy; | ||||
| 		if (s->cy > screen_size_y(s) - 1) | ||||
| 			s->cy = screen_size_y(s) - 1; | ||||
| 		if (gc != NULL) | ||||
| 			memcpy(gc, &s->saved_cell, sizeof *gc); | ||||
| 	} | ||||
|  | ||||
| 	if (s->saved_grid == NULL) | ||||
| 	/* If not in the alternate screen, do nothing more. */ | ||||
| 	if (s->saved_grid == NULL) { | ||||
| 		if (s->cx > screen_size_x(s) - 1) | ||||
| 			s->cx = screen_size_x(s) - 1; | ||||
| 		if (s->cy > screen_size_y(s) - 1) | ||||
| 			s->cy = screen_size_y(s) - 1; | ||||
| 		return; | ||||
| 	sx = screen_size_x(s); | ||||
| 	sy = screen_size_y(s); | ||||
|  | ||||
| 	/* | ||||
| 	 * If the current size is bigger, temporarily resize to the old size | ||||
| 	 * before copying back. | ||||
| 	 */ | ||||
| 	if (sy > s->saved_grid->sy) | ||||
| 		screen_resize(s, sx, s->saved_grid->sy, 1); | ||||
| 	} | ||||
|  | ||||
| 	/* Restore the saved grid. */ | ||||
| 	grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, sy); | ||||
| 	grid_duplicate_lines(s->grid, screen_hsize(s), s->saved_grid, 0, | ||||
| 	    s->saved_grid->sy); | ||||
|  | ||||
| 	/* | ||||
| 	 * Turn history back on (so resize can use it) and then resize back to | ||||
| @@ -611,9 +612,13 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) | ||||
| 	 */ | ||||
| 	if (s->saved_flags & GRID_HISTORY) | ||||
| 		s->grid->flags |= GRID_HISTORY; | ||||
| 	if (sy > s->saved_grid->sy || sx != s->saved_grid->sx) | ||||
| 		screen_resize(s, sx, sy, 1); | ||||
| 	screen_resize(s, sx, sy, 1); | ||||
|  | ||||
| 	grid_destroy(s->saved_grid); | ||||
| 	s->saved_grid = NULL; | ||||
|  | ||||
| 	if (s->cx > screen_size_x(s) - 1) | ||||
| 		s->cx = screen_size_x(s) - 1; | ||||
| 	if (s->cy > screen_size_y(s) - 1) | ||||
| 		s->cy = screen_size_y(s) - 1; | ||||
| } | ||||
|   | ||||
							
								
								
									
										132
									
								
								server-client.c
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								server-client.c
									
									
									
									
									
								
							| @@ -21,7 +21,6 @@ | ||||
| #include <sys/uio.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <event.h> | ||||
| #include <fcntl.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| @@ -40,6 +39,7 @@ static void	server_client_repeat_timer(int, short, void *); | ||||
| static void	server_client_click_timer(int, short, void *); | ||||
| static void	server_client_check_exit(struct client *); | ||||
| static void	server_client_check_redraw(struct client *); | ||||
| static void	server_client_check_modes(struct client *); | ||||
| static void	server_client_set_title(struct client *); | ||||
| static void	server_client_reset_state(struct client *); | ||||
| static int	server_client_assume_paste(struct session *); | ||||
| @@ -48,12 +48,6 @@ static void	server_client_dispatch(struct imsg *, void *); | ||||
| static void	server_client_dispatch_command(struct client *, struct imsg *); | ||||
| static void	server_client_dispatch_identify(struct client *, struct imsg *); | ||||
| static void	server_client_dispatch_shell(struct client *); | ||||
| static void	server_client_dispatch_write_ready(struct client *, | ||||
| 		    struct imsg *); | ||||
| static void	server_client_dispatch_read_data(struct client *, | ||||
| 		    struct imsg *); | ||||
| static void	server_client_dispatch_read_done(struct client *, | ||||
| 		    struct imsg *); | ||||
|  | ||||
| /* Compare client windows. */ | ||||
| static int | ||||
| @@ -310,6 +304,7 @@ server_client_lost(struct client *c) | ||||
|  | ||||
| 	free(c->term_name); | ||||
| 	free(c->term_type); | ||||
| 	tty_term_free_list(c->term_caps, c->term_ncaps); | ||||
|  | ||||
| 	status_free(c); | ||||
|  | ||||
| @@ -1353,6 +1348,7 @@ server_client_loop(void) | ||||
| 	TAILQ_FOREACH(c, &clients, entry) { | ||||
| 		server_client_check_exit(c); | ||||
| 		if (c->session != NULL) { | ||||
| 			server_client_check_modes(c); | ||||
| 			server_client_check_redraw(c); | ||||
| 			server_client_reset_state(c); | ||||
| 		} | ||||
| @@ -1777,11 +1773,11 @@ server_client_check_exit(struct client *c) | ||||
|  | ||||
| 	switch (c->exit_type) { | ||||
| 	case CLIENT_EXIT_RETURN: | ||||
| 		if (c->exit_message != NULL) { | ||||
| 		if (c->exit_message != NULL) | ||||
| 			msize = strlen(c->exit_message) + 1; | ||||
| 			size = (sizeof c->retval) + msize; | ||||
| 		} else | ||||
| 			size = (sizeof c->retval); | ||||
| 		else | ||||
| 			msize = 0; | ||||
| 		size = (sizeof c->retval) + msize; | ||||
| 		data = xmalloc(size); | ||||
| 		memcpy(data, &c->retval, sizeof c->retval); | ||||
| 		if (c->exit_message != NULL) | ||||
| @@ -1808,6 +1804,28 @@ server_client_redraw_timer(__unused int fd, __unused short events, | ||||
| 	log_debug("redraw timer fired"); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Check if modes need to be updated. Only modes in the current window are | ||||
|  * updated and it is done when the status line is redrawn. | ||||
|  */ | ||||
| static void | ||||
| server_client_check_modes(struct client *c) | ||||
| { | ||||
| 	struct window			*w = c->session->curw->window; | ||||
| 	struct window_pane		*wp; | ||||
| 	struct window_mode_entry	*wme; | ||||
|  | ||||
| 	if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) | ||||
| 		return; | ||||
| 	if (~c->flags & CLIENT_REDRAWSTATUS) | ||||
| 		return; | ||||
| 	TAILQ_FOREACH(wp, &w->panes, entry) { | ||||
| 		wme = TAILQ_FIRST(&wp->modes); | ||||
| 		if (wme != NULL && wme->mode->update != NULL) | ||||
| 			wme->mode->update(wme); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Check for client redraws. */ | ||||
| static void | ||||
| server_client_check_redraw(struct client *c) | ||||
| @@ -1977,16 +1995,17 @@ server_client_dispatch(struct imsg *imsg, void *arg) | ||||
| 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
|  | ||||
| 	switch (imsg->hdr.type) { | ||||
| 	case MSG_IDENTIFY_CLIENTPID: | ||||
| 	case MSG_IDENTIFY_CWD: | ||||
| 	case MSG_IDENTIFY_ENVIRON: | ||||
| 	case MSG_IDENTIFY_FEATURES: | ||||
| 	case MSG_IDENTIFY_FLAGS: | ||||
| 	case MSG_IDENTIFY_LONGFLAGS: | ||||
| 	case MSG_IDENTIFY_TERM: | ||||
| 	case MSG_IDENTIFY_TTYNAME: | ||||
| 	case MSG_IDENTIFY_CWD: | ||||
| 	case MSG_IDENTIFY_STDIN: | ||||
| 	case MSG_IDENTIFY_STDOUT: | ||||
| 	case MSG_IDENTIFY_ENVIRON: | ||||
| 	case MSG_IDENTIFY_CLIENTPID: | ||||
| 	case MSG_IDENTIFY_TERM: | ||||
| 	case MSG_IDENTIFY_TERMINFO: | ||||
| 	case MSG_IDENTIFY_TTYNAME: | ||||
| 	case MSG_IDENTIFY_DONE: | ||||
| 		server_client_dispatch_identify(c, imsg); | ||||
| 		break; | ||||
| @@ -2044,13 +2063,13 @@ server_client_dispatch(struct imsg *imsg, void *arg) | ||||
| 		server_client_dispatch_shell(c); | ||||
| 		break; | ||||
| 	case MSG_WRITE_READY: | ||||
| 		server_client_dispatch_write_ready(c, imsg); | ||||
| 		file_write_ready(&c->files, imsg); | ||||
| 		break; | ||||
| 	case MSG_READ: | ||||
| 		server_client_dispatch_read_data(c, imsg); | ||||
| 		file_read_data(&c->files, imsg); | ||||
| 		break; | ||||
| 	case MSG_READ_DONE: | ||||
| 		server_client_dispatch_read_done(c, imsg); | ||||
| 		file_read_done(&c->files, imsg); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| @@ -2180,6 +2199,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) | ||||
| 			c->term_name = xstrdup(data); | ||||
| 		log_debug("client %p IDENTIFY_TERM %s", c, data); | ||||
| 		break; | ||||
| 	case MSG_IDENTIFY_TERMINFO: | ||||
| 		if (datalen == 0 || data[datalen - 1] != '\0') | ||||
| 			fatalx("bad MSG_IDENTIFY_TERMINFO string"); | ||||
| 		c->term_caps = xreallocarray(c->term_caps, c->term_ncaps + 1, | ||||
| 		    sizeof *c->term_caps); | ||||
| 		c->term_caps[c->term_ncaps++] = xstrdup(data); | ||||
| 		log_debug("client %p IDENTIFY_TERMINFO %s", c, data); | ||||
| 		break; | ||||
| 	case MSG_IDENTIFY_TTYNAME: | ||||
| 		if (datalen == 0 || data[datalen - 1] != '\0') | ||||
| 			fatalx("bad MSG_IDENTIFY_TTYNAME string"); | ||||
| @@ -2280,71 +2307,6 @@ server_client_dispatch_shell(struct client *c) | ||||
| 	proc_kill_peer(c->peer); | ||||
| } | ||||
|  | ||||
| /* Handle write ready message. */ | ||||
| static void | ||||
| server_client_dispatch_write_ready(struct client *c, struct imsg *imsg) | ||||
| { | ||||
| 	struct msg_write_ready	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	struct client_file	 find, *cf; | ||||
|  | ||||
| 	if (msglen != sizeof *msg) | ||||
| 		fatalx("bad MSG_WRITE_READY size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) | ||||
| 		return; | ||||
| 	if (msg->error != 0) { | ||||
| 		cf->error = msg->error; | ||||
| 		file_fire_done(cf); | ||||
| 	} else | ||||
| 		file_push(cf); | ||||
| } | ||||
|  | ||||
| /* Handle read data message. */ | ||||
| static void | ||||
| server_client_dispatch_read_data(struct client *c, struct imsg *imsg) | ||||
| { | ||||
| 	struct msg_read_data	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	struct client_file	 find, *cf; | ||||
| 	void			*bdata = msg + 1; | ||||
| 	size_t			 bsize = msglen - sizeof *msg; | ||||
|  | ||||
| 	if (msglen < sizeof *msg) | ||||
| 		fatalx("bad MSG_READ_DATA size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize); | ||||
| 	if (cf->error == 0) { | ||||
| 		if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { | ||||
| 			cf->error = ENOMEM; | ||||
| 			file_fire_done(cf); | ||||
| 		} else | ||||
| 			file_fire_read(cf); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* Handle read done message. */ | ||||
| static void | ||||
| server_client_dispatch_read_done(struct client *c, struct imsg *imsg) | ||||
| { | ||||
| 	struct msg_read_done	*msg = imsg->data; | ||||
| 	size_t			 msglen = imsg->hdr.len - IMSG_HEADER_SIZE; | ||||
| 	struct client_file	 find, *cf; | ||||
|  | ||||
| 	if (msglen != sizeof *msg) | ||||
| 		fatalx("bad MSG_READ_DONE size"); | ||||
| 	find.stream = msg->stream; | ||||
| 	if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	log_debug("%s: file %d read done", c->name, cf->stream); | ||||
| 	cf->error = msg->error; | ||||
| 	file_fire_done(cf); | ||||
| } | ||||
|  | ||||
| /* Get client working directory. */ | ||||
| const char * | ||||
| server_client_get_cwd(struct client *c, struct session *s) | ||||
| @@ -2432,6 +2394,8 @@ server_client_get_flags(struct client *c) | ||||
| 	*s = '\0'; | ||||
| 	if (c->flags & CLIENT_ATTACHED) | ||||
| 		strlcat(s, "attached,", sizeof s); | ||||
| 	if (c->flags & CLIENT_FOCUSED) | ||||
| 		strlcat(s, "focused,", sizeof s); | ||||
| 	if (c->flags & CLIENT_CONTROL) | ||||
| 		strlcat(s, "control-mode,", sizeof s); | ||||
| 	if (c->flags & CLIENT_IGNORESIZE) | ||||
|   | ||||
							
								
								
									
										41
									
								
								server-fn.c
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								server-fn.c
									
									
									
									
									
								
							| @@ -312,6 +312,7 @@ server_destroy_pane(struct window_pane *wp, int notify) | ||||
| 	struct grid_cell	 gc; | ||||
| 	time_t			 t; | ||||
| 	char			 tim[26]; | ||||
| 	int			 remain_on_exit; | ||||
|  | ||||
| 	if (wp->fd != -1) { | ||||
| #ifdef HAVE_UTEMPTER | ||||
| @@ -323,10 +324,17 @@ server_destroy_pane(struct window_pane *wp, int notify) | ||||
| 		wp->fd = -1; | ||||
| 	} | ||||
|  | ||||
| 	if (options_get_number(wp->options, "remain-on-exit")) { | ||||
| 		if (~wp->flags & PANE_STATUSREADY) | ||||
| 			return; | ||||
|  | ||||
| 	remain_on_exit = options_get_number(wp->options, "remain-on-exit"); | ||||
| 	if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY)) | ||||
| 		return; | ||||
| 	switch (remain_on_exit) { | ||||
| 	case 0: | ||||
| 		break; | ||||
| 	case 2: | ||||
| 		if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0) | ||||
| 			break; | ||||
| 		/* FALLTHROUGH */ | ||||
| 	case 1: | ||||
| 		if (wp->flags & PANE_STATUSDRAWN) | ||||
| 			return; | ||||
| 		wp->flags |= PANE_STATUSDRAWN; | ||||
| @@ -394,9 +402,8 @@ server_destroy_session_group(struct session *s) | ||||
| static struct session * | ||||
| server_next_session(struct session *s) | ||||
| { | ||||
| 	struct session *s_loop, *s_out; | ||||
| 	struct session *s_loop, *s_out = NULL; | ||||
|  | ||||
| 	s_out = NULL; | ||||
| 	RB_FOREACH(s_loop, sessions, &sessions) { | ||||
| 		if (s_loop == s) | ||||
| 			continue; | ||||
| @@ -407,17 +414,35 @@ server_next_session(struct session *s) | ||||
| 	return (s_out); | ||||
| } | ||||
|  | ||||
| static struct session * | ||||
| server_next_detached_session(struct session *s) | ||||
| { | ||||
| 	struct session *s_loop, *s_out = NULL; | ||||
|  | ||||
| 	RB_FOREACH(s_loop, sessions, &sessions) { | ||||
| 		if (s_loop == s || s_loop->attached) | ||||
| 			continue; | ||||
| 		if (s_out == NULL || | ||||
| 		    timercmp(&s_loop->activity_time, &s_out->activity_time, <)) | ||||
| 			s_out = s_loop; | ||||
| 	} | ||||
| 	return (s_out); | ||||
| } | ||||
|  | ||||
| void | ||||
| server_destroy_session(struct session *s) | ||||
| { | ||||
| 	struct client	*c; | ||||
| 	struct session	*s_new; | ||||
| 	int		 detach_on_destroy; | ||||
|  | ||||
| 	if (!options_get_number(s->options, "detach-on-destroy")) | ||||
| 	detach_on_destroy = options_get_number(s->options, "detach-on-destroy"); | ||||
| 	if (detach_on_destroy == 0) | ||||
| 		s_new = server_next_session(s); | ||||
| 	else if (detach_on_destroy == 2) | ||||
| 		s_new = server_next_detached_session(s); | ||||
| 	else | ||||
| 		s_new = NULL; | ||||
|  | ||||
| 	TAILQ_FOREACH(c, &clients, entry) { | ||||
| 		if (c->session != s) | ||||
| 			continue; | ||||
|   | ||||
							
								
								
									
										30
									
								
								server.c
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								server.c
									
									
									
									
									
								
							| @@ -24,7 +24,6 @@ | ||||
| #include <sys/wait.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <event.h> | ||||
| #include <fcntl.h> | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| @@ -155,35 +154,22 @@ int | ||||
| server_start(struct tmuxproc *client, int flags, struct event_base *base, | ||||
|     int lockfd, char *lockfile) | ||||
| { | ||||
| 	int		 pair[2]; | ||||
| 	sigset_t	 set, oldset; | ||||
| 	struct client	*c = NULL; | ||||
| 	char		*cause = NULL; | ||||
| 	int		  fd; | ||||
| 	sigset_t	  set, oldset; | ||||
| 	struct client	 *c = NULL; | ||||
| 	char		 *cause = NULL; | ||||
|  | ||||
| 	sigfillset(&set); | ||||
| 	sigprocmask(SIG_BLOCK, &set, &oldset); | ||||
|  | ||||
| 	if (~flags & CLIENT_NOFORK) { | ||||
| 		if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) | ||||
| 			fatal("socketpair failed"); | ||||
|  | ||||
| 		switch (fork()) { | ||||
| 		case -1: | ||||
| 			fatal("fork failed"); | ||||
| 		case 0: | ||||
| 			break; | ||||
| 		default: | ||||
| 		if (proc_fork_and_daemon(&fd) != 0) { | ||||
| 			sigprocmask(SIG_SETMASK, &oldset, NULL); | ||||
| 			close(pair[1]); | ||||
| 			return (pair[0]); | ||||
| 			return (fd); | ||||
| 		} | ||||
| 		close(pair[0]); | ||||
| 		if (daemon(1, 0) != 0) | ||||
| 			fatal("daemon failed"); | ||||
| 	} | ||||
|  | ||||
| 	server_client_flags = flags; | ||||
| 	proc_clear_signals(client, 0); | ||||
| 	server_client_flags = flags; | ||||
|  | ||||
| 	if (event_reinit(base) != 0) | ||||
| 		fatalx("event_reinit failed"); | ||||
| @@ -212,7 +198,7 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base, | ||||
| 	if (server_fd != -1) | ||||
| 		server_update_socket(); | ||||
| 	if (~flags & CLIENT_NOFORK) | ||||
| 		c = server_client_create(pair[1]); | ||||
| 		c = server_client_create(fd); | ||||
| 	else | ||||
| 		options_set_number(global_options, "exit-empty", 0); | ||||
|  | ||||
|   | ||||
							
								
								
									
										15
									
								
								spawn.c
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								spawn.c
									
									
									
									
									
								
							| @@ -182,7 +182,7 @@ spawn_window(struct spawn_context *sc, char **cause) | ||||
| 			    NULL); | ||||
| 			options_set_number(w->options, "automatic-rename", 0); | ||||
| 		} else | ||||
| 			w->name = xstrdup(default_window_name(w)); | ||||
| 			w->name = default_window_name(w); | ||||
| 	} | ||||
|  | ||||
| 	/* Switch to the new window if required. */ | ||||
| @@ -263,8 +263,9 @@ spawn_pane(struct spawn_context *sc, char **cause) | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Now we have a pane with nothing running in it ready for the new process. | ||||
| 	 * Work out the command and arguments and store the working directory. | ||||
| 	 * Now we have a pane with nothing running in it ready for the new | ||||
| 	 * process. Work out the command and arguments and store the working | ||||
| 	 * directory. | ||||
| 	 */ | ||||
| 	if (sc->argc == 0 && (~sc->flags & SPAWN_RESPAWN)) { | ||||
| 		cmd = options_get_string(s->options, "default-command"); | ||||
| @@ -377,10 +378,10 @@ spawn_pane(struct spawn_context *sc, char **cause) | ||||
| 	 * Child process. Change to the working directory or home if that | ||||
| 	 * fails. | ||||
| 	 */ | ||||
| 	if (chdir(new_wp->cwd) != 0) { | ||||
| 		if ((tmp = find_home()) == NULL || chdir(tmp) != 0) | ||||
| 			chdir("/"); | ||||
| 	} | ||||
| 	if (chdir(new_wp->cwd) != 0 && | ||||
| 	    ((tmp = find_home()) == NULL || chdir(tmp) != 0) && | ||||
| 	    chdir("/") != 0) | ||||
| 		fatal("chdir failed"); | ||||
|  | ||||
| 	/* | ||||
| 	 * Update terminal escape characters from the session if available and | ||||
|   | ||||
							
								
								
									
										58
									
								
								status.c
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								status.c
									
									
									
									
									
								
							| @@ -543,7 +543,7 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, | ||||
|     prompt_free_cb freecb, void *data, int flags) | ||||
| { | ||||
| 	struct format_tree	*ft; | ||||
| 	char			*tmp, *cp; | ||||
| 	char			*tmp; | ||||
|  | ||||
| 	if (fs != NULL) | ||||
| 		ft = format_create_from_state(NULL, c, fs); | ||||
| @@ -563,7 +563,13 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, | ||||
|  | ||||
| 	c->prompt_string = format_expand_time(ft, msg); | ||||
|  | ||||
| 	c->prompt_buffer = utf8_fromcstr(tmp); | ||||
| 	if (flags & PROMPT_INCREMENTAL) { | ||||
| 		c->prompt_last = xstrdup(tmp); | ||||
| 		c->prompt_buffer = utf8_fromcstr(""); | ||||
| 	} else { | ||||
| 		c->prompt_last = NULL; | ||||
| 		c->prompt_buffer = utf8_fromcstr(tmp); | ||||
| 	} | ||||
| 	c->prompt_index = utf8_strlen(c->prompt_buffer); | ||||
|  | ||||
| 	c->prompt_inputcb = inputcb; | ||||
| @@ -579,11 +585,8 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, | ||||
| 		c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); | ||||
| 	c->flags |= CLIENT_REDRAWSTATUS; | ||||
|  | ||||
| 	if ((flags & PROMPT_INCREMENTAL) && *tmp != '\0') { | ||||
| 		xasprintf(&cp, "=%s", tmp); | ||||
| 		c->prompt_inputcb(c, c->prompt_data, cp, 0); | ||||
| 		free(cp); | ||||
| 	} | ||||
| 	if (flags & PROMPT_INCREMENTAL) | ||||
| 		c->prompt_inputcb(c, c->prompt_data, "=", 0); | ||||
|  | ||||
| 	free(tmp); | ||||
| 	format_free(ft); | ||||
| @@ -599,6 +602,9 @@ status_prompt_clear(struct client *c) | ||||
| 	if (c->prompt_freecb != NULL && c->prompt_data != NULL) | ||||
| 		c->prompt_freecb(c->prompt_data); | ||||
|  | ||||
| 	free(c->prompt_last); | ||||
| 	c->prompt_last = NULL; | ||||
|  | ||||
| 	free(c->prompt_string); | ||||
| 	c->prompt_string = NULL; | ||||
|  | ||||
| @@ -1260,17 +1266,27 @@ process_key: | ||||
| 			status_prompt_clear(c); | ||||
| 		break; | ||||
| 	case '\022': /* C-r */ | ||||
| 		if (c->prompt_flags & PROMPT_INCREMENTAL) { | ||||
| 		if (~c->prompt_flags & PROMPT_INCREMENTAL) | ||||
| 			break; | ||||
| 		if (c->prompt_buffer[0].size == 0) { | ||||
| 			prefix = '='; | ||||
| 			free (c->prompt_buffer); | ||||
| 			c->prompt_buffer = utf8_fromcstr(c->prompt_last); | ||||
| 			c->prompt_index = utf8_strlen(c->prompt_buffer); | ||||
| 		} else | ||||
| 			prefix = '-'; | ||||
| 			goto changed; | ||||
| 		} | ||||
| 		break; | ||||
| 		goto changed; | ||||
| 	case '\023': /* C-s */ | ||||
| 		if (c->prompt_flags & PROMPT_INCREMENTAL) { | ||||
| 		if (~c->prompt_flags & PROMPT_INCREMENTAL) | ||||
| 			break; | ||||
| 		if (c->prompt_buffer[0].size == 0) { | ||||
| 			prefix = '='; | ||||
| 			free (c->prompt_buffer); | ||||
| 			c->prompt_buffer = utf8_fromcstr(c->prompt_last); | ||||
| 			c->prompt_index = utf8_strlen(c->prompt_buffer); | ||||
| 		} else | ||||
| 			prefix = '+'; | ||||
| 			goto changed; | ||||
| 		} | ||||
| 		break; | ||||
| 		goto changed; | ||||
| 	default: | ||||
| 		goto append_key; | ||||
| 	} | ||||
| @@ -1303,12 +1319,14 @@ append_key: | ||||
| 	} | ||||
|  | ||||
| 	if (c->prompt_flags & PROMPT_SINGLE) { | ||||
| 		s = utf8_tocstr(c->prompt_buffer); | ||||
| 		if (strlen(s) != 1) | ||||
| 		if (utf8_strlen(c->prompt_buffer) != 1) | ||||
| 			status_prompt_clear(c); | ||||
| 		else if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) | ||||
| 			status_prompt_clear(c); | ||||
| 		free(s); | ||||
| 		else { | ||||
| 			s = utf8_tocstr(c->prompt_buffer); | ||||
| 			if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) | ||||
| 				status_prompt_clear(c); | ||||
| 			free(s); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| changed: | ||||
|   | ||||
							
								
								
									
										277
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										277
									
								
								tmux.1
									
									
									
									
									
								
							| @@ -191,6 +191,11 @@ directories are missing). | ||||
| Behave as a login shell. | ||||
| This flag currently has no effect and is for compatibility with other shells | ||||
| when using tmux as a login shell. | ||||
| .It Fl N | ||||
| Do not start the server even if the command would normally do so (for example | ||||
| .Ic new-session | ||||
| or | ||||
| .Ic start-server ) . | ||||
| .It Fl S Ar socket-path | ||||
| Specify a full alternative path to the server socket. | ||||
| If | ||||
| @@ -517,6 +522,67 @@ Commands separated by semicolons together form a | ||||
| - if a command in the sequence encounters an error, no subsequent commands are | ||||
| executed. | ||||
| .Pp | ||||
| It is recommended that a semicolon used as a command separator should be | ||||
| written as an individual token, for example from | ||||
| .Xr sh 1 : | ||||
| .Bd -literal -offset indent | ||||
| $ tmux neww \\; splitw | ||||
| .Ed | ||||
| .Pp | ||||
| Or: | ||||
| .Bd -literal -offset indent | ||||
| $ tmux neww ';' splitw | ||||
| .Ed | ||||
| .Pp | ||||
| Or from the tmux command prompt: | ||||
| .Bd -literal -offset indent | ||||
| neww ; splitw | ||||
| .Ed | ||||
| .Pp | ||||
| However, a trailing semicolon is also interpreted as a command separator, | ||||
| for example in these | ||||
| .Xr sh 1 | ||||
| commands: | ||||
| .Bd -literal -offset indent | ||||
| $ tmux neww\e\e; splitw | ||||
| .Ed | ||||
| .Pp | ||||
| Or: | ||||
| .Bd -literal -offset indent | ||||
| $ tmux 'neww;' splitw | ||||
| .Ed | ||||
| .Pp | ||||
| As in these examples, when running tmux from the shell extra care must be taken | ||||
| to properly quote semicolons: | ||||
| .Bl -enum -offset Ds | ||||
| .It | ||||
| Semicolons that should be interpreted as a command separator | ||||
| should be escaped according to the shell conventions. | ||||
| For | ||||
| .Xr sh 1 | ||||
| this typically means quoted (such as | ||||
| .Ql neww ';' splitw ) | ||||
| or escaped (such as | ||||
| .Ql neww \e\e\e\e; splitw ) . | ||||
| .It | ||||
| Individual semicolons or trailing semicolons that should be interpreted as | ||||
| arguments should be escaped twice: once according to the shell conventions and | ||||
| a second time for | ||||
| .Nm ; | ||||
| for example: | ||||
| .Bd -literal -offset indent | ||||
| $ tmux neww 'foo\e\e;' bar | ||||
| $ tmux neww foo\e\e\e\e; bar | ||||
| .Ed | ||||
| .It | ||||
| Semicolons that are not individual tokens or trailing another token should only | ||||
| be escaped once according to shell conventions; for example: | ||||
| .Bd -literal -offset indent | ||||
| $ tmux neww 'foo-;-bar' | ||||
| $ tmux neww foo-\e\e;-bar | ||||
| .Ed | ||||
| .El | ||||
| .Pp | ||||
| Comments are marked by the unquoted # character - any remaining text after a | ||||
| comment is ignored until the end of the line. | ||||
| .Pp | ||||
| @@ -867,12 +933,12 @@ arguments are | ||||
| commands. | ||||
| This may be a single argument passed to the shell, for example: | ||||
| .Bd -literal -offset indent | ||||
| new-window 'vi /etc/passwd' | ||||
| new-window 'vi ~/.tmux.conf' | ||||
| .Ed | ||||
| .Pp | ||||
| Will run: | ||||
| .Bd -literal -offset indent | ||||
| /bin/sh -c 'vi /etc/passwd' | ||||
| /bin/sh -c 'vi ~/.tmux.conf' | ||||
| .Ed | ||||
| .Pp | ||||
| Additionally, the | ||||
| @@ -889,7 +955,7 @@ to be given as multiple arguments and executed directly (without | ||||
| This can avoid issues with shell quoting. | ||||
| For example: | ||||
| .Bd -literal -offset indent | ||||
| $ tmux new-window vi /etc/passwd | ||||
| $ tmux new-window vi ~/.tmux.conf | ||||
| .Ed | ||||
| .Pp | ||||
| Will run | ||||
| @@ -935,7 +1001,7 @@ $ tmux kill-window -t :1 | ||||
|  | ||||
| $ tmux new-window \e; split-window -d | ||||
|  | ||||
| $ tmux new-session -d 'vi /etc/passwd' \e; split-window -d \e; attach | ||||
| $ tmux new-session -d 'vi ~/.tmux.conf' \e; split-window -d \e; attach | ||||
| .Ed | ||||
| .Sh CLIENTS AND SESSIONS | ||||
| The | ||||
| @@ -1357,7 +1423,7 @@ a pane ID such as | ||||
| .Ql %0 ; | ||||
| .Ql %* | ||||
| for all panes in the attached session; | ||||
| an window ID such as | ||||
| a window ID such as | ||||
| .Ql @0 ; | ||||
| or | ||||
| .Ql @* | ||||
| @@ -1648,10 +1714,15 @@ The following commands are supported in copy mode: | ||||
| .It Li "page-down" Ta "C-f" Ta "PageDown" | ||||
| .It Li "page-down-and-cancel" Ta "" Ta "" | ||||
| .It Li "page-up" Ta "C-b" Ta "PageUp" | ||||
| .It Li "pipe [<command>] [<prefix>]" Ta "" Ta "" | ||||
| .It Li "pipe-no-clear [<command>] [<prefix>]" Ta "" Ta "" | ||||
| .It Li "pipe-and-cancel [<command>] [<prefix>]" Ta "" Ta "" | ||||
| .It Li "previous-matching-bracket" Ta "" Ta "M-C-b" | ||||
| .It Li "previous-paragraph" Ta "{" Ta "M-{" | ||||
| .It Li "previous-space" Ta "B" Ta "" | ||||
| .It Li "previous-word" Ta "b" Ta "M-b" | ||||
| .It Li "rectangle-on" Ta "" Ta "" | ||||
| .It Li "rectangle-off" Ta "" Ta "" | ||||
| .It Li "rectangle-toggle" Ta "v" Ta "R" | ||||
| .It Li "refresh-from-pane" Ta "r" Ta "r" | ||||
| .It Li "scroll-down" Ta "C-e" Ta "C-Down" | ||||
| @@ -1701,7 +1772,9 @@ so buffers are named | ||||
| .Ql buffer1 | ||||
| and so on). | ||||
| Pipe commands take a command argument which is the command to which the | ||||
| copied text is piped. | ||||
| selected text is piped. | ||||
| .Ql copy-pipe | ||||
| variants also copy the selection. | ||||
| The | ||||
| .Ql -and-cancel | ||||
| variants of some commands exit copy mode after they have completed (for copy | ||||
| @@ -2098,7 +2171,7 @@ starts without the option information. | ||||
| This command works only if at least one client is attached. | ||||
| .It Xo | ||||
| .Ic display-panes | ||||
| .Op Fl b | ||||
| .Op Fl bN | ||||
| .Op Fl d Ar duration | ||||
| .Op Fl t Ar target-client | ||||
| .Op Ar template | ||||
| @@ -2111,7 +2184,9 @@ See the | ||||
| and | ||||
| .Ic display-panes-active-colour | ||||
| session options. | ||||
| The indicator is closed when a key is pressed or | ||||
| The indicator is closed when a key is pressed (unless | ||||
| .Fl N | ||||
| is given) or | ||||
| .Ar duration | ||||
| milliseconds have passed. | ||||
| If | ||||
| @@ -2339,7 +2414,7 @@ the | ||||
| .Ic base-index | ||||
| option. | ||||
| .It Xo Ic new-window | ||||
| .Op Fl abdkP | ||||
| .Op Fl abdkPS | ||||
| .Op Fl c Ar start-directory | ||||
| .Op Fl e Ar environment | ||||
| .Op Fl F Ar format | ||||
| @@ -2368,6 +2443,14 @@ represents the window to be created; if the target already exists an error is | ||||
| shown, unless the | ||||
| .Fl k | ||||
| flag is used, in which case it is destroyed. | ||||
| If | ||||
| .Fl S | ||||
| is given and a window named | ||||
| .Ar window-name | ||||
| already exists, it is selected (unless | ||||
| .Fl d | ||||
| is also given in which case the command does nothing). | ||||
| .Pp | ||||
| .Ar shell-command | ||||
| is the command to execute. | ||||
| If | ||||
| @@ -3134,7 +3217,7 @@ abc123 | ||||
| Commands which set options are as follows: | ||||
| .Bl -tag -width Ds | ||||
| .It Xo Ic set-option | ||||
| .Op Fl aFgopqsuw | ||||
| .Op Fl aFgopqsuUw | ||||
| .Op Fl t Ar target-pane | ||||
| .Ar option Ar value | ||||
| .Xc | ||||
| @@ -3167,6 +3250,11 @@ flag unsets an option, so a session inherits the option from the global | ||||
| options (or with | ||||
| .Fl g , | ||||
| restores a global option to the default). | ||||
| .Fl U | ||||
| unsets an option (like | ||||
| .Fl u ) | ||||
| but if the option is a pane option also unsets the option on any panes in the | ||||
| window. | ||||
| .Ar value | ||||
| depends on the option and may be a number, a string, or a flag (on, off, or | ||||
| omitted to toggle). | ||||
| @@ -3559,12 +3647,16 @@ The default is 80x24. | ||||
| If enabled and the session is no longer attached to any clients, it is | ||||
| destroyed. | ||||
| .It Xo Ic detach-on-destroy | ||||
| .Op Ic on | off | ||||
| .Op Ic off | on | no-detached | ||||
| .Xc | ||||
| If on (the default), the client is detached when the session it is attached to | ||||
| is destroyed. | ||||
| If off, the client is switched to the most recently active of the remaining | ||||
| sessions. | ||||
| If | ||||
| .Ic no-detached , | ||||
| the client is detached only if there are no detached sessions; if detached | ||||
| sessions exist, the client is switched to the most recently active. | ||||
| .It Ic display-panes-active-colour Ar colour | ||||
| Set the colour used by the | ||||
| .Ic display-panes | ||||
| @@ -4058,12 +4150,6 @@ see the | ||||
| section. | ||||
| Attributes are ignored. | ||||
| .Pp | ||||
| .It Xo Ic synchronize-panes | ||||
| .Op Ic on | off | ||||
| .Xc | ||||
| Duplicate input to any pane to all other panes in the same window (only | ||||
| for panes that are not in any special mode). | ||||
| .Pp | ||||
| .It Ic window-status-activity-style Ar style | ||||
| Set status line style for windows with an activity alert. | ||||
| For how to specify | ||||
| @@ -4178,14 +4264,23 @@ interactive application starts and restores it on exit, so that any output | ||||
| visible before the application starts reappears unchanged after it exits. | ||||
| .Pp | ||||
| .It Xo Ic remain-on-exit | ||||
| .Op Ic on | off | ||||
| .Op Ic on | off | failed | ||||
| .Xc | ||||
| A pane with this flag set is not destroyed when the program running in it | ||||
| exits. | ||||
| If set to | ||||
| .Ic failed , | ||||
| then only when the program exit status is not zero. | ||||
| The pane may be reactivated with the | ||||
| .Ic respawn-pane | ||||
| command. | ||||
| .Pp | ||||
| .It Xo Ic synchronize-panes | ||||
| .Op Ic on | off | ||||
| .Xc | ||||
| Duplicate input to all other panes in the same window where this option is also | ||||
| on (only for panes that are not in any mode). | ||||
| .Pp | ||||
| .It Ic window-active-style Ar style | ||||
| Set the pane style when it is the active pane. | ||||
| For how to specify | ||||
| @@ -4589,7 +4684,9 @@ pads the string to a given width, for example | ||||
| will result in a width of at least 10 characters. | ||||
| A positive width pads on the left, a negative on the right. | ||||
| .Ql n | ||||
| expands to the length of the variable, for example | ||||
| expands to the length of the variable and | ||||
| .Ql w | ||||
| to its width when displayed, for example | ||||
| .Ql #{n:window_name} . | ||||
| .Pp | ||||
| Prefixing a time variable with | ||||
| @@ -4632,7 +4729,12 @@ of the variable respectively. | ||||
| .Ql q:\& | ||||
| will escape | ||||
| .Xr sh 1 | ||||
| special characters. | ||||
| special characters or with a | ||||
| .Ql h | ||||
| suffix, escape hash characters (so | ||||
| .Ql # | ||||
| becomes | ||||
| .Ql ## ) . | ||||
| .Ql E:\& | ||||
| will expand the format twice, for example | ||||
| .Ql #{E:status-left} | ||||
| @@ -4658,6 +4760,17 @@ For example, to get a list of windows formatted like the status line: | ||||
| #{W:#{E:window-status-format} ,#{E:window-status-current-format} } | ||||
| .Ed | ||||
| .Pp | ||||
| .Ql N:\& | ||||
| checks if a window (without any suffix or with the | ||||
| .Ql w | ||||
| suffix) or a session (with the | ||||
| .Ql s | ||||
| suffix) name exists, for example | ||||
| .Ql `N/w:foo` | ||||
| is replaced with 1 if a window named | ||||
| .Ql foo | ||||
| exists. | ||||
| .Pp | ||||
| A prefix of the form | ||||
| .Ql s/foo/bar/:\& | ||||
| will substitute | ||||
| @@ -4704,6 +4817,7 @@ will be replaced by | ||||
| The following variables are available, where appropriate: | ||||
| .Bl -column "XXXXXXXXXXXXXXXXXXX" "XXXXX" | ||||
| .It Sy "Variable name" Ta Sy "Alias" Ta Sy "Replaced with" | ||||
| .It Li "active_window_index" Ta "" Ta "Index of active window in session" | ||||
| .It Li "alternate_on" Ta "" Ta "1 if pane is in alternate screen" | ||||
| .It Li "alternate_saved_x" Ta "" Ta "Saved cursor X in alternate screen" | ||||
| .It Li "alternate_saved_y" Ta "" Ta "Saved cursor Y in alternate screen" | ||||
| @@ -4726,9 +4840,9 @@ The following variables are available, where appropriate: | ||||
| .It Li "client_prefix" Ta "" Ta "1 if prefix key has been pressed" | ||||
| .It Li "client_readonly" Ta "" Ta "1 if client is readonly" | ||||
| .It Li "client_session" Ta "" Ta "Name of the client's session" | ||||
| .It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" | ||||
| .It Li "client_termname" Ta "" Ta "Terminal name of client" | ||||
| .It Li "client_termtype" Ta "" Ta "Terminal type of client, if available" | ||||
| .It Li "client_termfeatures" Ta "" Ta "Terminal features of client, if any" | ||||
| .It Li "client_tty" Ta "" Ta "Pseudo terminal of client" | ||||
| .It Li "client_utf8" Ta "" Ta "1 if client supports UTF-8" | ||||
| .It Li "client_width" Ta "" Ta "Width of client" | ||||
| @@ -4737,6 +4851,7 @@ The following variables are available, where appropriate: | ||||
| .It Li "command_list_alias" Ta "" Ta "Command alias if listing commands" | ||||
| .It Li "command_list_name" Ta "" Ta "Command name if listing commands" | ||||
| .It Li "command_list_usage" Ta "" Ta "Command usage if listing commands" | ||||
| .It Li "config_files" Ta "" Ta "List of configuration files loaded" | ||||
| .It Li "copy_cursor_line" Ta "" Ta "Line the cursor is on in copy mode" | ||||
| .It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode" | ||||
| .It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode" | ||||
| @@ -4759,6 +4874,7 @@ The following variables are available, where appropriate: | ||||
| .It Li "insert_flag" Ta "" Ta "Pane insert flag" | ||||
| .It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" | ||||
| .It Li "keypad_flag" Ta "" Ta "Pane keypad flag" | ||||
| .It Li "last_window_index" Ta "" Ta "Index of last window in session" | ||||
| .It Li "line" Ta "" Ta "Line number in the list" | ||||
| .It Li "mouse_all_flag" Ta "" Ta "Pane mouse all flag" | ||||
| .It Li "mouse_any_flag" Ta "" Ta "Pane mouse any flag" | ||||
| @@ -4776,11 +4892,13 @@ The following variables are available, where appropriate: | ||||
| .It Li "pane_at_left" Ta "" Ta "1 if pane is at the left of window" | ||||
| .It Li "pane_at_right" Ta "" Ta "1 if pane is at the right of window" | ||||
| .It Li "pane_at_top" Ta "" Ta "1 if pane is at the top of window" | ||||
| .It Li "pane_bg" Ta "" Ta "Pane background colour" | ||||
| .It Li "pane_bottom" Ta "" Ta "Bottom of 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_dead_status" Ta "" Ta "Exit status of process in dead pane" | ||||
| .It Li "pane_fg" Ta "" Ta "Pane foreground colour" | ||||
| .It Li "pane_format" Ta "" Ta "1 if format is for a pane" | ||||
| .It Li "pane_height" Ta "" Ta "Height of pane" | ||||
| .It Li "pane_id" Ta "#D" Ta "Unique pane ID" | ||||
| @@ -4797,7 +4915,6 @@ The following variables are available, where appropriate: | ||||
| .It Li "pane_pipe" Ta "" Ta "1 if pane is being piped" | ||||
| .It Li "pane_right" Ta "" Ta "Right of pane" | ||||
| .It Li "pane_search_string" Ta "" Ta "Last search string in copy mode" | ||||
| .It Li "pane_skipped" Ta "" Ta "Bytes skipped as not visible in pane" | ||||
| .It Li "pane_start_command" Ta "" Ta "Command pane started with" | ||||
| .It Li "pane_synchronized" Ta "" Ta "1 if pane is synchronized" | ||||
| .It Li "pane_tabs" Ta "" Ta "Pane tab positions" | ||||
| @@ -4805,17 +4922,13 @@ The following variables are available, where appropriate: | ||||
| .It Li "pane_top" Ta "" Ta "Top of pane" | ||||
| .It Li "pane_tty" Ta "" Ta "Pseudo terminal of pane" | ||||
| .It Li "pane_width" Ta "" Ta "Width of pane" | ||||
| .It Li "pane_written" Ta "" Ta "Bytes written by pane (aside from redrawing)" | ||||
| .It Li "pid" Ta "" Ta "Server PID" | ||||
| .It Li "popup_key" Ta "" Ta "Key pressed in popup" | ||||
| .It Li "popup_mouse_x" Ta "" Ta "Mouse X position in popup" | ||||
| .It Li "popup_mouse_y" Ta "" Ta "Mouse Y position in popup" | ||||
| .It Li "rectangle_toggle" Ta "" Ta "1 if rectangle selection is activated" | ||||
| .It Li "scroll_position" Ta "" Ta "Scroll position in copy mode" | ||||
| .It Li "scroll_region_lower" Ta "" Ta "Bottom of scroll region in pane" | ||||
| .It Li "scroll_region_upper" Ta "" Ta "Top of scroll region in pane" | ||||
| .It Li "search_present" Ta "" Ta "1 if search started in copy mode" | ||||
| .It Li "search_match" Ta "" Ta "Search match if any" | ||||
| .It Li "search_present" Ta "" Ta "1 if search started in copy mode" | ||||
| .It Li "selection_active" Ta "" Ta "1 if selection started and changes with the cursor in copy mode" | ||||
| .It Li "selection_end_x" Ta "" Ta "X position of the end of the selection" | ||||
| .It Li "selection_end_y" Ta "" Ta "Y position of the end of the selection" | ||||
| @@ -4858,7 +4971,8 @@ The following variables are available, where appropriate: | ||||
| .It Li "window_cell_height" Ta "" Ta "Height of each cell in pixels" | ||||
| .It Li "window_cell_width" Ta "" Ta "Width of each cell in pixels" | ||||
| .It Li "window_end_flag" Ta "" Ta "1 if window has the highest index" | ||||
| .It Li "window_flags" Ta "#F" Ta "Window flags" | ||||
| .It Li "window_flags" Ta "#F" Ta "Window flags with # escaped as ##" | ||||
| .It Li "window_raw_flags" Ta "" Ta "Window flags with nothing escaped" | ||||
| .It Li "window_format" Ta "" Ta "1 if format is for a window" | ||||
| .It Li "window_height" Ta "" Ta "Height of window" | ||||
| .It Li "window_id" Ta "" Ta "Unique window ID" | ||||
| @@ -5386,6 +5500,28 @@ Both may be a row or column number, or one of the following special values: | ||||
| .It Li "S" Ta Fl y Ta "The line above or below the status line" | ||||
| .El | ||||
| .Pp | ||||
| Or a format, which is expanded including the following additional variables: | ||||
| .Bl -column "XXXXXXXXXXXXXXXXXXXXXXXXXX" -offset indent | ||||
| .It Sy "Variable name" Ta Sy "Replaced with" | ||||
| .It Li "popup_centre_x" Ta "Centered in the client" | ||||
| .It Li "popup_centre_y" Ta "Centered in the client" | ||||
| .It Li "popup_height" Ta "Height of menu or popup" | ||||
| .It Li "popup_mouse_bottom" Ta "Bottom of at the mouse" | ||||
| .It Li "popup_mouse_centre_x" Ta "Horizontal centre at the mouse" | ||||
| .It Li "popup_mouse_centre_y" Ta "Vertical centre at the mouse" | ||||
| .It Li "popup_mouse_top" Ta "Top at the mouse" | ||||
| .It Li "popup_mouse_x" Ta "Mouse X position" | ||||
| .It Li "popup_mouse_y" Ta "Mouse Y position" | ||||
| .It Li "popup_pane_bottom" Ta "Bottom of the pane" | ||||
| .It Li "popup_pane_left" Ta "Left of the pane" | ||||
| .It Li "popup_pane_right" Ta "Right of the pane" | ||||
| .It Li "popup_pane_top" Ta "Top of the pane" | ||||
| .It Li "popup_status_line_y" Ta "Above or below the status line" | ||||
| .It Li "popup_width" Ta "Width of menu or popup" | ||||
| .It Li "popup_window_status_line_x" Ta "At the window position in status line" | ||||
| .It Li "popup_window_status_line_y" Ta "At the status line showing the window" | ||||
| .El | ||||
| .Pp | ||||
| Each menu consists of items followed by a key shortcut shown in brackets. | ||||
| If the menu is too large to fit on the terminal, it is not displayed. | ||||
| Pressing the key shortcut chooses the corresponding item. | ||||
| @@ -5445,58 +5581,24 @@ lists the format variables and their values. | ||||
| forwards any input read from stdin to the empty pane given by | ||||
| .Ar target-pane . | ||||
| .It Xo Ic display-popup | ||||
| .Op Fl CEK | ||||
| .Op Fl CE | ||||
| .Op Fl c Ar target-client | ||||
| .Op Fl d Ar start-directory | ||||
| .Op Fl h Ar height | ||||
| .Op Fl R Ar shell-command | ||||
| .Op Fl t Ar target-pane | ||||
| .Op Fl w Ar width | ||||
| .Op Fl x Ar position | ||||
| .Op Fl y Ar position | ||||
| .Op Ar command Ar line Ar ... | ||||
| .Op Ar shell-command | ||||
| .Xc | ||||
| .D1 (alias: Ic popup ) | ||||
| Display a popup on | ||||
| Display a popup running | ||||
| .Ar shell-command | ||||
| on | ||||
| .Ar target-client . | ||||
| A popup is a rectangular box drawn over the top of any panes. | ||||
| Panes are not updated while a popup is present. | ||||
| The popup content may be given in two ways: | ||||
| .Bl -enum -offset Ds | ||||
| .It | ||||
| A set of lines as arguments. | ||||
| Each line is a format which is expanded using | ||||
| .Ar target-pane | ||||
| as the target. | ||||
| If a line contains newlines it is split into multiple lines. | ||||
| Lines may use styles, see the | ||||
| .Sx STYLES | ||||
| section. | ||||
| .It | ||||
| A shell command given by | ||||
| .Fl R | ||||
| which is run and any output shown in the pane. | ||||
| .El | ||||
| .Pp | ||||
| The first argument, | ||||
| .Ar command , | ||||
| is a | ||||
| .Nm | ||||
| command which is run when a key is pressed. | ||||
| The key is available in the | ||||
| .Ql popup_key | ||||
| format. | ||||
| After | ||||
| .Ar command | ||||
| is run, the popup is closed. | ||||
| It may be empty to discard any key presses. | ||||
| If | ||||
| .Fl K | ||||
| is given together with | ||||
| .Fl R , | ||||
| key presses are instead passed to the | ||||
| .Fl R | ||||
| shell command. | ||||
| .Fl E | ||||
| closes the popup automatically when | ||||
| .Ar shell-command | ||||
| @@ -5506,14 +5608,6 @@ Two | ||||
| closes the popup only if | ||||
| .Ar shell-command | ||||
| exited with success. | ||||
| With | ||||
| .Fl K , | ||||
| .Ql Escape | ||||
| and | ||||
| .Ql C-c | ||||
| close the popup unless | ||||
| .Fl E | ||||
| is also given. | ||||
| .Pp | ||||
| .Fl x | ||||
| and | ||||
| @@ -5526,11 +5620,7 @@ and | ||||
| .Fl h | ||||
| give the width and height - both may be a percentage (followed by | ||||
| .Ql % ) . | ||||
| If omitted, without | ||||
| .Fl R | ||||
| they are calculated from the given lines and with | ||||
| .Fl R | ||||
| they use half the terminal size. | ||||
| If omitted, half of the terminal size is used. | ||||
| .Pp | ||||
| The | ||||
| .Fl C | ||||
| @@ -5789,7 +5879,7 @@ Lock each client individually by running the command specified by the | ||||
| .Ic lock-command | ||||
| option. | ||||
| .It Xo Ic run-shell | ||||
| .Op Fl b | ||||
| .Op Fl bC | ||||
| .Op Fl d Ar delay | ||||
| .Op Fl t Ar target-pane | ||||
| .Op Ar shell-command | ||||
| @@ -5797,9 +5887,14 @@ option. | ||||
| .D1 (alias: Ic run ) | ||||
| Execute | ||||
| .Ar shell-command | ||||
| in the background without creating a window. | ||||
| Before being executed, shell-command is expanded using the rules specified in | ||||
| the | ||||
| or (with | ||||
| .Fl C ) | ||||
| a | ||||
| .Nm | ||||
| command in the background without creating a window. | ||||
| Before being executed, | ||||
| .Ar shell-command | ||||
| is expanded using the rules specified in the | ||||
| .Sx FORMATS | ||||
| section. | ||||
| With | ||||
| @@ -5809,11 +5904,13 @@ the command is run in the background. | ||||
| waits for | ||||
| .Ar delay | ||||
| seconds before starting the command. | ||||
| After the command finishes, any output to stdout is displayed in view mode (in | ||||
| the pane specified by | ||||
| If | ||||
| .Fl C | ||||
| is not given, any output to stdout is displayed in view mode (in the pane | ||||
| specified by | ||||
| .Fl t | ||||
| or the current pane if omitted). | ||||
| If the command doesn't return success, the exit status is also displayed. | ||||
| or the current pane if omitted) after the command finishes. | ||||
| If the command fails, the exit status is also displayed. | ||||
| .It Xo Ic wait-for | ||||
| .Op Fl L | S | U | ||||
| .Ar channel | ||||
| @@ -5876,6 +5973,10 @@ option should be used. | ||||
| An existing extension that tells | ||||
| .Nm | ||||
| the terminal supports default colours. | ||||
| .It Em \&Bidi | ||||
| Tell | ||||
| .Nm | ||||
| that the terminal supports the VTE bidirectional text extensions. | ||||
| .It Em \&Cs , Cr | ||||
| Set the cursor colour. | ||||
| The first takes a single string argument and is used to set the colour; | ||||
|   | ||||
							
								
								
									
										57
									
								
								tmux.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								tmux.c
									
									
									
									
									
								
							| @@ -21,7 +21,6 @@ | ||||
| #include <sys/utsname.h> | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <event.h> | ||||
| #include <fcntl.h> | ||||
| #include <langinfo.h> | ||||
| #include <locale.h> | ||||
| @@ -54,7 +53,7 @@ static __dead void | ||||
| usage(void) | ||||
| { | ||||
| 	fprintf(stderr, | ||||
| 	    "usage: %s [-2CDluvV] [-c shell-command] [-f file] [-L socket-name]\n" | ||||
| 	    "usage: %s [-2CDlNuvV] [-c shell-command] [-f file] [-L socket-name]\n" | ||||
| 	    "            [-S socket-path] [-T features] [command [flags]]\n", | ||||
| 	    getprogname()); | ||||
| 	exit(1); | ||||
| @@ -139,11 +138,12 @@ expand_path(const char *path, const char *home) | ||||
| 	return (xstrdup(path)); | ||||
| } | ||||
|  | ||||
| void | ||||
| expand_paths(const char *s, char ***paths, u_int *n) | ||||
| static void | ||||
| expand_paths(const char *s, char ***paths, u_int *n, int ignore_errors) | ||||
| { | ||||
| 	const char	*home = find_home(); | ||||
| 	char		*copy, *next, *tmp, resolved[PATH_MAX], *expanded; | ||||
| 	char		*path; | ||||
| 	u_int		 i; | ||||
|  | ||||
| 	*paths = NULL; | ||||
| @@ -159,20 +159,26 @@ expand_paths(const char *s, char ***paths, u_int *n) | ||||
| 		if (realpath(expanded, resolved) == NULL) { | ||||
| 			log_debug("%s: realpath(\"%s\") failed: %s", __func__, | ||||
| 			    expanded, strerror(errno)); | ||||
| 			if (ignore_errors) { | ||||
| 				free(expanded); | ||||
| 				continue; | ||||
| 			} | ||||
| 			path = expanded; | ||||
| 		} else { | ||||
| 			path = xstrdup(resolved); | ||||
| 			free(expanded); | ||||
| 			continue; | ||||
| 		} | ||||
| 		free(expanded); | ||||
| 		for (i = 0; i < *n; i++) { | ||||
| 			if (strcmp(resolved, (*paths)[i]) == 0) | ||||
| 			if (strcmp(path, (*paths)[i]) == 0) | ||||
| 				break; | ||||
| 		} | ||||
| 		if (i != *n) { | ||||
| 			log_debug("%s: duplicate path: %s", __func__, resolved); | ||||
| 			log_debug("%s: duplicate path: %s", __func__, path); | ||||
| 			free(path); | ||||
| 			continue; | ||||
| 		} | ||||
| 		*paths = xreallocarray(*paths, (*n) + 1, sizeof *paths); | ||||
| 		(*paths)[(*n)++] = xstrdup(resolved); | ||||
| 		(*paths)[(*n)++] = path; | ||||
| 	} | ||||
| 	free(copy); | ||||
| } | ||||
| @@ -190,7 +196,7 @@ make_label(const char *label, char **cause) | ||||
| 		label = "default"; | ||||
| 	uid = getuid(); | ||||
|  | ||||
| 	expand_paths(TMUX_SOCK, &paths, &n); | ||||
| 	expand_paths(TMUX_SOCK, &paths, &n, 1); | ||||
| 	if (n == 0) { | ||||
| 		xasprintf(cause, "no suitable socket path"); | ||||
| 		return (NULL); | ||||
| @@ -321,10 +327,11 @@ main(int argc, char **argv) | ||||
| { | ||||
| 	char					*path = NULL, *label = NULL; | ||||
| 	char					*cause, **var; | ||||
| 	const char				*s, *shell, *cwd; | ||||
| 	const char				*s, *cwd; | ||||
| 	int					 opt, keys, feat = 0; | ||||
| 	uint64_t				 flags = 0; | ||||
| 	const struct options_table_entry	*oe; | ||||
| 	u_int					 i; | ||||
|  | ||||
| 	if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL && | ||||
| 	    setlocale(LC_CTYPE, "C.UTF-8") == NULL) { | ||||
| @@ -341,7 +348,14 @@ main(int argc, char **argv) | ||||
| 	if (**argv == '-') | ||||
| 		flags = CLIENT_LOGIN; | ||||
|  | ||||
| 	while ((opt = getopt(argc, argv, "2c:CDdf:lL:qS:T:uUvV")) != -1) { | ||||
| 	global_environ = environ_create(); | ||||
| 	for (var = environ; *var != NULL; var++) | ||||
| 		environ_put(global_environ, *var, 0); | ||||
| 	if ((cwd = find_cwd()) != NULL) | ||||
| 		environ_set(global_environ, "PWD", 0, "%s", cwd); | ||||
| 	expand_paths(TMUX_CONF, &cfg_files, &cfg_nfiles, 1); | ||||
|  | ||||
| 	while ((opt = getopt(argc, argv, "2c:CDdf:lL:NqS:T:uUvV")) != -1) { | ||||
| 		switch (opt) { | ||||
| 		case '2': | ||||
| 			tty_add_features(&feat, "256", ":,"); | ||||
| @@ -359,7 +373,11 @@ main(int argc, char **argv) | ||||
| 				flags |= CLIENT_CONTROL; | ||||
| 			break; | ||||
| 		case 'f': | ||||
| 			set_cfg_file(optarg); | ||||
| 			for (i = 0; i < cfg_nfiles; i++) | ||||
| 				free(cfg_files[i]); | ||||
| 			free(cfg_files); | ||||
| 			expand_paths(optarg, &cfg_files, &cfg_nfiles, 0); | ||||
| 			cfg_quiet = 0; | ||||
| 			break; | ||||
|  		case 'V': | ||||
| 			printf("%s %s\n", getprogname(), getversion()); | ||||
| @@ -371,6 +389,9 @@ main(int argc, char **argv) | ||||
| 			free(label); | ||||
| 			label = xstrdup(optarg); | ||||
| 			break; | ||||
| 		case 'N': | ||||
| 			flags |= CLIENT_NOSTARTSERVER; | ||||
| 			break; | ||||
| 		case 'q': | ||||
| 			break; | ||||
| 		case 'S': | ||||
| @@ -426,12 +447,6 @@ main(int argc, char **argv) | ||||
| 			flags |= CLIENT_UTF8; | ||||
| 	} | ||||
|  | ||||
| 	global_environ = environ_create(); | ||||
| 	for (var = environ; *var != NULL; var++) | ||||
| 		environ_put(global_environ, *var, 0); | ||||
| 	if ((cwd = find_cwd()) != NULL) | ||||
| 		environ_set(global_environ, "PWD", 0, "%s", cwd); | ||||
|  | ||||
| 	global_options = options_create(NULL); | ||||
| 	global_s_options = options_create(NULL); | ||||
| 	global_w_options = options_create(NULL); | ||||
| @@ -448,8 +463,8 @@ main(int argc, char **argv) | ||||
| 	 * The default shell comes from SHELL or from the user's passwd entry | ||||
| 	 * if available. | ||||
| 	 */ | ||||
| 	shell = getshell(); | ||||
| 	options_set_string(global_s_options, "default-shell", 0, "%s", shell); | ||||
| 	options_set_string(global_s_options, "default-shell", 0, "%s", | ||||
| 	    getshell()); | ||||
|  | ||||
| 	/* Override keys to vi if VISUAL or EDITOR are set. */ | ||||
| 	if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { | ||||
|   | ||||
							
								
								
									
										156
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -22,7 +22,6 @@ | ||||
| #include <sys/time.h> | ||||
| #include <sys/uio.h> | ||||
|  | ||||
| #include <event.h> | ||||
| #include <limits.h> | ||||
| #include <stdarg.h> | ||||
| #include <stdio.h> | ||||
| @@ -57,8 +56,8 @@ struct mouse_event; | ||||
| struct options; | ||||
| struct options_array_item; | ||||
| struct options_entry; | ||||
| struct screen_write_collect_item; | ||||
| struct screen_write_collect_line; | ||||
| struct screen_write_citem; | ||||
| struct screen_write_cline; | ||||
| struct screen_write_ctx; | ||||
| struct session; | ||||
| struct tty_ctx; | ||||
| @@ -263,6 +262,7 @@ enum tty_code_code { | ||||
| 	TTYC_AX, | ||||
| 	TTYC_BCE, | ||||
| 	TTYC_BEL, | ||||
| 	TTYC_BIDI, | ||||
| 	TTYC_BLINK, | ||||
| 	TTYC_BOLD, | ||||
| 	TTYC_CIVIS, | ||||
| @@ -501,6 +501,7 @@ enum msgtype { | ||||
| 	MSG_IDENTIFY_FEATURES, | ||||
| 	MSG_IDENTIFY_STDOUT, | ||||
| 	MSG_IDENTIFY_LONGFLAGS, | ||||
| 	MSG_IDENTIFY_TERMINFO, | ||||
|  | ||||
| 	MSG_COMMAND = 200, | ||||
| 	MSG_DETACH, | ||||
| @@ -728,6 +729,13 @@ struct grid { | ||||
| 	struct grid_line	*linedata; | ||||
| }; | ||||
|  | ||||
| /* Virtual cursor in a grid. */ | ||||
| struct grid_reader { | ||||
| 	struct grid	*gd; | ||||
| 	u_int		 cx; | ||||
| 	u_int		 cy; | ||||
| }; | ||||
|  | ||||
| /* Style alignment. */ | ||||
| enum style_align { | ||||
| 	STYLE_ALIGN_DEFAULT, | ||||
| @@ -789,55 +797,51 @@ struct style { | ||||
| struct screen_sel; | ||||
| struct screen_titles; | ||||
| struct screen { | ||||
| 	char			*title; | ||||
| 	char			*path; | ||||
| 	struct screen_titles	*titles; | ||||
| 	char				*title; | ||||
| 	char				*path; | ||||
| 	struct screen_titles		*titles; | ||||
|  | ||||
| 	struct grid		*grid;		/* grid data */ | ||||
| 	struct grid			*grid;	  /* grid data */ | ||||
|  | ||||
| 	u_int			 cx;		/* cursor x */ | ||||
| 	u_int			 cy;		/* cursor y */ | ||||
| 	u_int				 cx;	  /* cursor x */ | ||||
| 	u_int				 cy;	  /* cursor y */ | ||||
|  | ||||
| 	u_int			 cstyle;	/* cursor style */ | ||||
| 	char			*ccolour;	/* cursor colour string */ | ||||
| 	u_int				 cstyle;  /* cursor style */ | ||||
| 	char				*ccolour; /* cursor colour string */ | ||||
|  | ||||
| 	u_int			 rupper;	/* scroll region top */ | ||||
| 	u_int			 rlower;	/* scroll region bottom */ | ||||
| 	u_int				 rupper;  /* scroll region top */ | ||||
| 	u_int				 rlower;  /* scroll region bottom */ | ||||
|  | ||||
| 	int			 mode; | ||||
| 	int				 mode; | ||||
|  | ||||
| 	u_int			 saved_cx; | ||||
| 	u_int			 saved_cy; | ||||
| 	struct grid		*saved_grid; | ||||
| 	struct grid_cell	 saved_cell; | ||||
| 	int			 saved_flags; | ||||
| 	u_int				 saved_cx; | ||||
| 	u_int				 saved_cy; | ||||
| 	struct grid			*saved_grid; | ||||
| 	struct grid_cell		 saved_cell; | ||||
| 	int				 saved_flags; | ||||
|  | ||||
| 	bitstr_t		*tabs; | ||||
| 	struct screen_sel	*sel; | ||||
| 	bitstr_t			*tabs; | ||||
| 	struct screen_sel		*sel; | ||||
|  | ||||
| 	struct screen_write_collect_line *write_list; | ||||
| 	struct screen_write_cline	*write_list; | ||||
| }; | ||||
|  | ||||
| /* Screen write context. */ | ||||
| typedef void (*screen_write_init_ctx_cb)(struct screen_write_ctx *, | ||||
|     struct tty_ctx *); | ||||
| struct screen_write_ctx { | ||||
| 	struct window_pane	*wp; | ||||
| 	struct screen		*s; | ||||
| 	struct window_pane		*wp; | ||||
| 	struct screen			*s; | ||||
|  | ||||
| 	int			 flags; | ||||
| 	int				 flags; | ||||
| #define SCREEN_WRITE_SYNC 0x1 | ||||
|  | ||||
| 	screen_write_init_ctx_cb init_ctx_cb; | ||||
| 	void			*arg; | ||||
| 	screen_write_init_ctx_cb	 init_ctx_cb; | ||||
| 	void				*arg; | ||||
|  | ||||
| 	struct screen_write_collect_item *item; | ||||
| 	u_int			 scrolled; | ||||
| 	u_int			 bg; | ||||
|  | ||||
| 	u_int			 cells; | ||||
| 	u_int			 written; | ||||
| 	u_int			 skipped; | ||||
| 	struct screen_write_citem	*item; | ||||
| 	u_int				 scrolled; | ||||
| 	u_int				 bg; | ||||
| }; | ||||
|  | ||||
| /* Screen redraw context. */ | ||||
| @@ -889,6 +893,7 @@ struct window_mode { | ||||
| 			     struct cmd_find_state *, struct args *); | ||||
| 	void		 (*free)(struct window_mode_entry *); | ||||
| 	void		 (*resize)(struct window_mode_entry *, u_int, u_int); | ||||
| 	void		 (*update)(struct window_mode_entry *); | ||||
| 	void		 (*key)(struct window_mode_entry *, struct client *, | ||||
| 			     struct session *, struct winlink *, key_code, | ||||
| 			     struct mouse_event *); | ||||
| @@ -995,9 +1000,6 @@ struct window_pane { | ||||
| 	char		*searchstr; | ||||
| 	int		 searchregex; | ||||
|  | ||||
| 	size_t		 written; | ||||
| 	size_t		 skipped; | ||||
|  | ||||
| 	int		 border_gc_set; | ||||
| 	struct grid_cell border_gc; | ||||
|  | ||||
| @@ -1539,6 +1541,8 @@ typedef void (*client_file_cb) (struct client *, const char *, int, int, | ||||
|     struct evbuffer *, void *); | ||||
| struct client_file { | ||||
| 	struct client			*c; | ||||
| 	struct tmuxpeer			*peer; | ||||
| 	struct client_files		*tree; | ||||
| 	int				 references; | ||||
| 	int				 stream; | ||||
|  | ||||
| @@ -1601,6 +1605,8 @@ struct client { | ||||
| 	char		*term_name; | ||||
| 	int		 term_features; | ||||
| 	char		*term_type; | ||||
| 	char	       **term_caps; | ||||
| 	u_int		 term_ncaps; | ||||
|  | ||||
| 	char		*ttyname; | ||||
| 	struct tty	 tty; | ||||
| @@ -1629,7 +1635,7 @@ struct client { | ||||
| #define CLIENT_DEAD 0x200 | ||||
| #define CLIENT_REDRAWBORDERS 0x400 | ||||
| #define CLIENT_READONLY 0x800 | ||||
| /* 0x1000 unused */ | ||||
| #define CLIENT_NOSTARTSERVER 0x1000 | ||||
| #define CLIENT_CONTROL 0x2000 | ||||
| #define CLIENT_CONTROLCONTROL 0x4000 | ||||
| #define CLIENT_FOCUSED 0x8000 | ||||
| @@ -1687,6 +1693,7 @@ struct client { | ||||
|  | ||||
| 	char		*prompt_string; | ||||
| 	struct utf8_data *prompt_buffer; | ||||
| 	char		*prompt_last; | ||||
| 	size_t		 prompt_index; | ||||
| 	prompt_input_cb	 prompt_inputcb; | ||||
| 	prompt_free_cb	 prompt_freecb; | ||||
| @@ -1881,8 +1888,6 @@ const char	*sig2name(int); | ||||
| const char	*find_cwd(void); | ||||
| const char	*find_home(void); | ||||
| const char	*getversion(void); | ||||
| void		 expand_paths(const char *, char ***, u_int *); | ||||
|  | ||||
|  | ||||
| /* proc.c */ | ||||
| struct imsg; | ||||
| @@ -1897,16 +1902,19 @@ struct tmuxpeer *proc_add_peer(struct tmuxproc *, int, | ||||
| void	proc_remove_peer(struct tmuxpeer *); | ||||
| void	proc_kill_peer(struct tmuxpeer *); | ||||
| void	proc_toggle_log(struct tmuxproc *); | ||||
| pid_t	proc_fork_and_daemon(int *); | ||||
|  | ||||
| /* cfg.c */ | ||||
| extern int cfg_finished; | ||||
| extern struct client *cfg_client; | ||||
| extern char **cfg_files; | ||||
| extern u_int cfg_nfiles; | ||||
| extern int cfg_quiet; | ||||
| void	start_cfg(void); | ||||
| int	load_cfg(const char *, struct client *, struct cmdq_item *, int, | ||||
| 	    struct cmdq_item **); | ||||
| int	load_cfg_from_buffer(const void *, size_t, const char *, | ||||
| 	    struct client *, struct cmdq_item *, int, struct cmdq_item **); | ||||
| void	set_cfg_file(const char *); | ||||
| void printflike(1, 2) cfg_add_cause(const char *, ...); | ||||
| void	cfg_print_causes(struct cmdq_item *); | ||||
| void	cfg_show_causes(struct session *); | ||||
| @@ -1937,7 +1945,7 @@ char		*paste_make_sample(struct paste_buffer *); | ||||
| #define FORMAT_WINDOW 0x40000000U | ||||
| struct format_tree; | ||||
| struct format_modifier; | ||||
| typedef char *(*format_cb)(struct format_tree *); | ||||
| typedef void *(*format_cb)(struct format_tree *); | ||||
| const char	*format_skip(const char *, const char *); | ||||
| int		 format_true(const char *); | ||||
| struct format_tree *format_create(struct client *, struct cmdq_item *, int, | ||||
| @@ -2060,9 +2068,9 @@ typedef void (*job_free_cb) (void *); | ||||
| #define JOB_NOWAIT 0x1 | ||||
| #define JOB_KEEPWRITE 0x2 | ||||
| #define JOB_PTY 0x4 | ||||
| struct job	*job_run(const char *, struct session *, const char *, | ||||
| 		     job_update_cb, job_complete_cb, job_free_cb, void *, int, | ||||
| 		     int, int); | ||||
| struct job	*job_run(const char *, int, char **, struct session *, | ||||
| 		     const char *, job_update_cb, job_complete_cb, job_free_cb, | ||||
| 		     void *, int, int, int); | ||||
| void		 job_free(struct job *); | ||||
| void		 job_resize(struct job *, u_int, u_int); | ||||
| void		 job_check_died(pid_t, int); | ||||
| @@ -2163,8 +2171,12 @@ extern struct tty_terms tty_terms; | ||||
| u_int		 tty_term_ncodes(void); | ||||
| void		 tty_term_apply(struct tty_term *, const char *, int); | ||||
| void		 tty_term_apply_overrides(struct tty_term *); | ||||
| struct tty_term *tty_term_create(struct tty *, char *, int *, int, char **); | ||||
| struct tty_term *tty_term_create(struct tty *, char *, char **, u_int, int *, | ||||
| 		     char **); | ||||
| void		 tty_term_free(struct tty_term *); | ||||
| int		 tty_term_read_list(const char *, int, char ***, u_int *, | ||||
| 		     char **); | ||||
| void		 tty_term_free_list(char **, u_int); | ||||
| int		 tty_term_has(struct tty_term *, enum tty_code_code); | ||||
| const char	*tty_term_string(struct tty_term *, enum tty_code_code); | ||||
| const char	*tty_term_string1(struct tty_term *, enum tty_code_code, int); | ||||
| @@ -2370,7 +2382,10 @@ void	alerts_check_session(struct session *); | ||||
| /* file.c */ | ||||
| int	 file_cmp(struct client_file *, struct client_file *); | ||||
| RB_PROTOTYPE(client_files, client_file, entry, file_cmp); | ||||
| struct client_file *file_create(struct client *, int, client_file_cb, void *); | ||||
| struct client_file *file_create_with_peer(struct tmuxpeer *, | ||||
| 	    struct client_files *, int, client_file_cb, void *); | ||||
| struct client_file *file_create_with_client(struct client *, int, | ||||
| 	    client_file_cb, void *); | ||||
| void	 file_free(struct client_file *); | ||||
| void	 file_fire_done(struct client_file *); | ||||
| void	 file_fire_read(struct client_file *); | ||||
| @@ -2383,6 +2398,16 @@ void	 file_write(struct client *, const char *, int, const void *, size_t, | ||||
| 	     client_file_cb, void *); | ||||
| void	 file_read(struct client *, const char *, client_file_cb, void *); | ||||
| void	 file_push(struct client_file *); | ||||
| int	 file_write_left(struct client_files *); | ||||
| void	 file_write_open(struct client_files *, struct tmuxpeer *, | ||||
| 	     struct imsg *, int, int, client_file_cb, void *); | ||||
| void	 file_write_data(struct client_files *, struct imsg *); | ||||
| void	 file_write_close(struct client_files *, struct imsg *); | ||||
| void	 file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *, | ||||
| 	     int, int, client_file_cb, void *); | ||||
| void	 file_write_ready(struct client_files *, struct imsg *); | ||||
| void	 file_read_data(struct client_files *, struct imsg *); | ||||
| void	 file_read_done(struct client_files *, struct imsg *); | ||||
|  | ||||
| /* server.c */ | ||||
| extern struct tmuxproc *server_proc; | ||||
| @@ -2509,6 +2534,7 @@ const char *colour_tostring(int); | ||||
| int	 colour_fromstring(const char *s); | ||||
| int	 colour_256toRGB(int); | ||||
| int	 colour_256to16(int); | ||||
| int	 colour_byname(const char *); | ||||
|  | ||||
| /* attributes.c */ | ||||
| const char *attributes_tostring(int); | ||||
| @@ -2549,6 +2575,26 @@ void	 grid_wrap_position(struct grid *, u_int, u_int, u_int *, u_int *); | ||||
| void	 grid_unwrap_position(struct grid *, u_int *, u_int *, u_int, u_int); | ||||
| u_int	 grid_line_length(struct grid *, u_int); | ||||
|  | ||||
| /* grid-reader.c */ | ||||
| void	 grid_reader_start(struct grid_reader *, struct grid *, u_int, u_int); | ||||
| void	 grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *); | ||||
| u_int	 grid_reader_line_length(struct grid_reader *); | ||||
| int	 grid_reader_in_set(struct grid_reader *, const char *); | ||||
| void	 grid_reader_cursor_right(struct grid_reader *, int, int); | ||||
| void	 grid_reader_cursor_left(struct grid_reader *); | ||||
| void	 grid_reader_cursor_down(struct grid_reader *); | ||||
| void	 grid_reader_cursor_up(struct grid_reader *); | ||||
| void	 grid_reader_cursor_start_of_line(struct grid_reader *, int); | ||||
| void	 grid_reader_cursor_end_of_line(struct grid_reader *, int, int); | ||||
| void	 grid_reader_cursor_next_word(struct grid_reader *, const char *); | ||||
| void	 grid_reader_cursor_next_word_end(struct grid_reader *, const char *); | ||||
| void	 grid_reader_cursor_previous_word(struct grid_reader *, const char *, | ||||
| 	     int); | ||||
| int	 grid_reader_cursor_jump(struct grid_reader *, | ||||
| 	     const struct utf8_data *); | ||||
| int	 grid_reader_cursor_jump_back(struct grid_reader *, | ||||
| 	     const struct utf8_data *); | ||||
|  | ||||
| /* grid-view.c */ | ||||
| void	 grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); | ||||
| void	 grid_view_set_cell(struct grid *, u_int, u_int, | ||||
| @@ -2668,7 +2714,6 @@ void	 screen_alternate_off(struct screen *, struct grid_cell *, int); | ||||
| /* window.c */ | ||||
| extern struct windows windows; | ||||
| extern struct window_pane_tree all_window_panes; | ||||
| extern const struct window_mode *all_window_modes[]; | ||||
| int		 window_cmp(struct window *, struct window *); | ||||
| RB_PROTOTYPE(windows, window, entry, window_cmp); | ||||
| int		 winlink_cmp(struct winlink *, struct winlink *); | ||||
| @@ -2739,7 +2784,7 @@ int		 window_pane_key(struct window_pane *, struct client *, | ||||
| int		 window_pane_visible(struct window_pane *); | ||||
| u_int		 window_pane_search(struct window_pane *, const char *, int, | ||||
| 		     int); | ||||
| const char	*window_printable_flags(struct winlink *); | ||||
| const char	*window_printable_flags(struct winlink *, int); | ||||
| struct window_pane *window_pane_find_up(struct window_pane *); | ||||
| struct window_pane *window_pane_find_down(struct window_pane *); | ||||
| struct window_pane *window_pane_find_left(struct window_pane *); | ||||
| @@ -2995,18 +3040,13 @@ int		 menu_display(struct menu *, int, struct cmdq_item *, u_int, | ||||
| 		    menu_choice_cb, void *); | ||||
|  | ||||
| /* popup.c */ | ||||
| #define POPUP_WRITEKEYS 0x1 | ||||
| #define POPUP_CLOSEEXIT 0x2 | ||||
| #define POPUP_CLOSEEXITZERO 0x4 | ||||
| #define POPUP_CLOSEEXIT 0x1 | ||||
| #define POPUP_CLOSEEXITZERO 0x2 | ||||
| typedef void (*popup_close_cb)(int, void *); | ||||
| typedef void (*popup_finish_edit_cb)(char *, size_t, void *); | ||||
| u_int		 popup_width(struct cmdq_item *, u_int, const char **, | ||||
| 		    struct client *, struct cmd_find_state *); | ||||
| u_int		 popup_height(u_int, const char **); | ||||
| int		 popup_display(int, struct cmdq_item *, u_int, u_int, u_int, | ||||
| 		    u_int, u_int, const char **, const char *, const char *, | ||||
| 		    const char *, struct client *, struct cmd_find_state *, | ||||
| 		    popup_close_cb, void *); | ||||
| 		    u_int, const char *, int, char **, const char *, | ||||
| 		    struct client *, struct session *, popup_close_cb, void *); | ||||
| int		 popup_editor(struct client *, const char *, size_t, | ||||
| 		    popup_finish_edit_cb, void *); | ||||
|  | ||||
|   | ||||
							
								
								
									
										166
									
								
								tty-term.c
									
									
									
									
									
								
							
							
						
						
									
										166
									
								
								tty-term.c
									
									
									
									
									
								
							| @@ -61,6 +61,7 @@ static const struct tty_term_code_entry tty_term_codes[] = { | ||||
| 	[TTYC_AX] = { TTYCODE_FLAG, "AX" }, | ||||
| 	[TTYC_BCE] = { TTYCODE_FLAG, "bce" }, | ||||
| 	[TTYC_BEL] = { TTYCODE_STRING, "bel" }, | ||||
| 	[TTYC_BIDI] = { TTYCODE_STRING, "Bidi" }, | ||||
| 	[TTYC_BLINK] = { TTYCODE_STRING, "blink" }, | ||||
| 	[TTYC_BOLD] = { TTYCODE_STRING, "bold" }, | ||||
| 	[TTYC_CIVIS] = { TTYCODE_STRING, "civis" }, | ||||
| @@ -452,7 +453,8 @@ tty_term_apply_overrides(struct tty_term *term) | ||||
| } | ||||
|  | ||||
| struct tty_term * | ||||
| tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) | ||||
| tty_term_create(struct tty *tty, char *name, char **caps, u_int ncaps, | ||||
|     int *feat, char **cause) | ||||
| { | ||||
| 	struct tty_term				*term; | ||||
| 	const struct tty_term_code_entry	*ent; | ||||
| @@ -460,10 +462,9 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) | ||||
| 	struct options_entry			*o; | ||||
| 	struct options_array_item		*a; | ||||
| 	union options_value			*ov; | ||||
| 	u_int					 i; | ||||
| 	int		 			 n, error; | ||||
| 	const char				*s, *acs; | ||||
| 	size_t					 offset; | ||||
| 	u_int					 i, j; | ||||
| 	const char				*s, *acs, *value; | ||||
| 	size_t					 offset, namelen; | ||||
| 	char					*first; | ||||
|  | ||||
| 	log_debug("adding term %s", name); | ||||
| @@ -474,57 +475,38 @@ tty_term_create(struct tty *tty, char *name, int *feat, int fd, char **cause) | ||||
| 	term->codes = xcalloc(tty_term_ncodes(), sizeof *term->codes); | ||||
| 	LIST_INSERT_HEAD(&tty_terms, term, entry); | ||||
|  | ||||
| 	/* Set up curses terminal. */ | ||||
| 	if (setupterm(name, fd, &error) != OK) { | ||||
| 		switch (error) { | ||||
| 		case 1: | ||||
| 			xasprintf(cause, "can't use hardcopy terminal: %s", | ||||
| 			    name); | ||||
| 			break; | ||||
| 		case 0: | ||||
| 			xasprintf(cause, "missing or unsuitable terminal: %s", | ||||
| 			    name); | ||||
| 			break; | ||||
| 		case -1: | ||||
| 			xasprintf(cause, "can't find terminfo database"); | ||||
| 			break; | ||||
| 		default: | ||||
| 			xasprintf(cause, "unknown error"); | ||||
| 			break; | ||||
| 		} | ||||
| 		goto error; | ||||
| 	} | ||||
|  | ||||
| 	/* Fill in codes. */ | ||||
| 	for (i = 0; i < tty_term_ncodes(); i++) { | ||||
| 		ent = &tty_term_codes[i]; | ||||
| 	for (i = 0; i < ncaps; i++) { | ||||
| 		namelen = strcspn(caps[i], "="); | ||||
| 		if (namelen == 0) | ||||
| 			continue; | ||||
| 		value = caps[i] + namelen + 1; | ||||
|  | ||||
| 		code = &term->codes[i]; | ||||
| 		code->type = TTYCODE_NONE; | ||||
| 		switch (ent->type) { | ||||
| 		case TTYCODE_NONE: | ||||
| 			break; | ||||
| 		case TTYCODE_STRING: | ||||
| 			s = tigetstr((char *) ent->name); | ||||
| 			if (s == NULL || s == (char *) -1) | ||||
| 		for (j = 0; j < tty_term_ncodes(); j++) { | ||||
| 			ent = &tty_term_codes[j]; | ||||
| 			if (strncmp(ent->name, caps[i], namelen) != 0) | ||||
| 				continue; | ||||
| 			if (ent->name[namelen] != '\0') | ||||
| 				continue; | ||||
|  | ||||
| 			code = &term->codes[j]; | ||||
| 			code->type = TTYCODE_NONE; | ||||
| 			switch (ent->type) { | ||||
| 			case TTYCODE_NONE: | ||||
| 				break; | ||||
| 			code->type = TTYCODE_STRING; | ||||
| 			code->value.string = tty_term_strip(s); | ||||
| 			break; | ||||
| 		case TTYCODE_NUMBER: | ||||
| 			n = tigetnum((char *) ent->name); | ||||
| 			if (n == -1 || n == -2) | ||||
| 			case TTYCODE_STRING: | ||||
| 				code->type = TTYCODE_STRING; | ||||
| 				code->value.string = tty_term_strip(value); | ||||
| 				break; | ||||
| 			code->type = TTYCODE_NUMBER; | ||||
| 			code->value.number = n; | ||||
| 			break; | ||||
| 		case TTYCODE_FLAG: | ||||
| 			n = tigetflag((char *) ent->name); | ||||
| 			if (n == -1) | ||||
| 			case TTYCODE_NUMBER: | ||||
| 				code->type = TTYCODE_NUMBER; | ||||
| 				code->value.number = atoi(value); | ||||
| 				break; | ||||
| 			code->type = TTYCODE_FLAG; | ||||
| 			code->value.flag = n; | ||||
| 			break; | ||||
| 			case TTYCODE_FLAG: | ||||
| 				code->type = TTYCODE_FLAG; | ||||
| 				code->value.flag = (*value == '1'); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -648,6 +630,88 @@ tty_term_free(struct tty_term *term) | ||||
| 	free(term); | ||||
| } | ||||
|  | ||||
| int | ||||
| tty_term_read_list(const char *name, int fd, char ***caps, u_int *ncaps, | ||||
|     char **cause) | ||||
| { | ||||
| 	const struct tty_term_code_entry	*ent; | ||||
| 	int					 error, n; | ||||
| 	u_int					 i; | ||||
| 	const char				*s; | ||||
| 	char					 tmp[11]; | ||||
|  | ||||
| 	if (setupterm(name, fd, &error) != OK) { | ||||
| 		switch (error) { | ||||
| 		case 1: | ||||
| 			xasprintf(cause, "can't use hardcopy terminal: %s", | ||||
| 			    name); | ||||
| 			break; | ||||
| 		case 0: | ||||
| 			xasprintf(cause, "missing or unsuitable terminal: %s", | ||||
| 			    name); | ||||
| 			break; | ||||
| 		case -1: | ||||
| 			xasprintf(cause, "can't find terminfo database"); | ||||
| 			break; | ||||
| 		default: | ||||
| 			xasprintf(cause, "unknown error"); | ||||
| 			break; | ||||
| 		} | ||||
| 		return (-1); | ||||
| 	} | ||||
|  | ||||
| 	*ncaps = 0; | ||||
| 	*caps = NULL; | ||||
|  | ||||
| 	for (i = 0; i < tty_term_ncodes(); i++) { | ||||
| 		ent = &tty_term_codes[i]; | ||||
| 		switch (ent->type) { | ||||
| 		case TTYCODE_NONE: | ||||
| 			break; | ||||
| 		case TTYCODE_STRING: | ||||
| 			s = tigetstr((char *)ent->name); | ||||
| 			if (s == NULL || s == (char *)-1) | ||||
| 				continue; | ||||
| 			break; | ||||
| 		case TTYCODE_NUMBER: | ||||
| 			n = tigetnum((char *)ent->name); | ||||
| 			if (n == -1 || n == -2) | ||||
| 				continue; | ||||
| 			xsnprintf(tmp, sizeof tmp, "%d", n); | ||||
| 			s = tmp; | ||||
| 			break; | ||||
| 		case TTYCODE_FLAG: | ||||
| 			n = tigetflag((char *) ent->name); | ||||
| 			if (n == -1) | ||||
| 				continue; | ||||
| 			if (n) | ||||
| 				s = "1"; | ||||
| 			else | ||||
| 				s = "0"; | ||||
| 			break; | ||||
| 		} | ||||
| 		*caps = xreallocarray(*caps, (*ncaps) + 1, sizeof **caps); | ||||
| 		xasprintf(&(*caps)[*ncaps], "%s=%s", ent->name, s); | ||||
| 		(*ncaps)++; | ||||
| 	} | ||||
|  | ||||
| #if !defined(NCURSES_VERSION_MAJOR) || NCURSES_VERSION_MAJOR > 5 || \ | ||||
|     (NCURSES_VERSION_MAJOR == 5 && NCURSES_VERSION_MINOR > 6) | ||||
| 	del_curterm(cur_term); | ||||
| #endif | ||||
| 	return (0); | ||||
| } | ||||
|  | ||||
| void | ||||
| tty_term_free_list(char **caps, u_int ncaps) | ||||
| { | ||||
| 	u_int	i; | ||||
|  | ||||
| 	for (i = 0; i < ncaps; i++) | ||||
| 		free(caps[i]); | ||||
| 	free(caps); | ||||
| } | ||||
|  | ||||
| int | ||||
| tty_term_has(struct tty_term *term, enum tty_code_code code) | ||||
| { | ||||
|   | ||||
							
								
								
									
										47
									
								
								tty.c
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								tty.c
									
									
									
									
									
								
							| @@ -249,8 +249,8 @@ tty_open(struct tty *tty, char **cause) | ||||
| { | ||||
| 	struct client	*c = tty->client; | ||||
|  | ||||
| 	tty->term = tty_term_create(tty, c->term_name, &c->term_features, | ||||
| 	    c->fd, cause); | ||||
| 	tty->term = tty_term_create(tty, c->term_name, c->term_caps, | ||||
| 	    c->term_ncaps, &c->term_features, cause); | ||||
| 	if (tty->term == NULL) { | ||||
| 		tty_close(tty); | ||||
| 		return (-1); | ||||
| @@ -694,28 +694,26 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) | ||||
| 	} | ||||
| 	if ((changed & ALL_MOUSE_MODES) && | ||||
| 	    tty_term_has(tty->term, TTYC_KMOUS)) { | ||||
| 		if ((mode & ALL_MOUSE_MODES) == 0) | ||||
| 		/* | ||||
| 		 * If the mouse modes have changed, clear any that are set and | ||||
| 		 * apply again. There are differences in how terminals track | ||||
| 		 * the various bits. | ||||
| 		 */ | ||||
| 		if (tty->mode & MODE_MOUSE_SGR) | ||||
| 			tty_puts(tty, "\033[?1006l"); | ||||
| 		if ((changed & MODE_MOUSE_STANDARD) && | ||||
| 		    (~mode & MODE_MOUSE_STANDARD)) | ||||
| 		if (tty->mode & MODE_MOUSE_STANDARD) | ||||
| 			tty_puts(tty, "\033[?1000l"); | ||||
| 		if ((changed & MODE_MOUSE_BUTTON) && | ||||
| 		    (~mode & MODE_MOUSE_BUTTON)) | ||||
| 		if (tty->mode & MODE_MOUSE_BUTTON) | ||||
| 			tty_puts(tty, "\033[?1002l"); | ||||
| 		if ((changed & MODE_MOUSE_ALL) && | ||||
| 		    (~mode & MODE_MOUSE_ALL)) | ||||
| 		if (tty->mode & MODE_MOUSE_ALL) | ||||
| 			tty_puts(tty, "\033[?1003l"); | ||||
|  | ||||
| 		if (mode & ALL_MOUSE_MODES) | ||||
| 			tty_puts(tty, "\033[?1006h"); | ||||
| 		if ((changed & MODE_MOUSE_STANDARD) && | ||||
| 		    (mode & MODE_MOUSE_STANDARD)) | ||||
| 		if (mode & MODE_MOUSE_STANDARD) | ||||
| 			tty_puts(tty, "\033[?1000h"); | ||||
| 		if ((changed & MODE_MOUSE_BUTTON) && | ||||
| 		    (mode & MODE_MOUSE_BUTTON)) | ||||
| 		if (mode & MODE_MOUSE_BUTTON) | ||||
| 			tty_puts(tty, "\033[?1002h"); | ||||
| 		if ((changed & MODE_MOUSE_ALL) && | ||||
| 		    (mode & MODE_MOUSE_ALL)) | ||||
| 		if (mode & MODE_MOUSE_ALL) | ||||
| 			tty_puts(tty, "\033[?1003h"); | ||||
| 	} | ||||
| 	if (changed & MODE_BRACKETPASTE) { | ||||
| @@ -1533,20 +1531,9 @@ tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) | ||||
| void | ||||
| tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) | ||||
| { | ||||
| 	if (ctx->bigger) { | ||||
| 		tty_draw_pane(tty, ctx, ctx->ocy); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg); | ||||
|  | ||||
| 	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); | ||||
|  | ||||
| 	if (tty_term_has(tty->term, TTYC_ECH) && | ||||
| 	    !tty_fake_bce(tty, &ctx->defaults, 8)) | ||||
| 		tty_putcode1(tty, TTYC_ECH, ctx->num); | ||||
| 	else | ||||
| 		tty_repeat_space(tty, ctx->num); | ||||
| 	tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg); | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -2449,7 +2436,7 @@ tty_check_fg(struct tty *tty, int *palette, struct grid_cell *gc) | ||||
| 	/* Is this a 256-colour colour? */ | ||||
| 	if (gc->fg & COLOUR_FLAG_256) { | ||||
| 		/* And not a 256 colour mode? */ | ||||
| 		if (colours != 256) { | ||||
| 		if (colours < 256) { | ||||
| 			gc->fg = colour_256to16(gc->fg); | ||||
| 			if (gc->fg & 8) { | ||||
| 				gc->fg &= 7; | ||||
| @@ -2502,7 +2489,7 @@ tty_check_bg(struct tty *tty, int *palette, struct grid_cell *gc) | ||||
| 		 * palette. Bold background doesn't exist portably, so just | ||||
| 		 * discard the bold bit if set. | ||||
| 		 */ | ||||
| 		if (colours != 256) { | ||||
| 		if (colours < 256) { | ||||
| 			gc->bg = colour_256to16(gc->bg); | ||||
| 			if (gc->bg & 8) { | ||||
| 				gc->bg &= 7; | ||||
|   | ||||
							
								
								
									
										8
									
								
								utf8.c
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								utf8.c
									
									
									
									
									
								
							| @@ -216,7 +216,11 @@ utf8_width(struct utf8_data *ud, int *width) | ||||
| { | ||||
| 	wchar_t	wc; | ||||
|  | ||||
| #ifdef HAVE_UTF8PROC | ||||
| 	switch (utf8proc_mbtowc(&wc, ud->data, ud->size)) { | ||||
| #else | ||||
| 	switch (mbtowc(&wc, ud->data, ud->size)) { | ||||
| #endif | ||||
| 	case -1: | ||||
| 		log_debug("UTF-8 %.*s, mbtowc() %d", (int)ud->size, ud->data, | ||||
| 		    errno); | ||||
| @@ -225,7 +229,11 @@ utf8_width(struct utf8_data *ud, int *width) | ||||
| 	case 0: | ||||
| 		return (UTF8_ERROR); | ||||
| 	} | ||||
| #ifdef HAVE_UTF8PROC | ||||
| 	*width = utf8proc_wcwidth(wc); | ||||
| #else | ||||
| 	*width = wcwidth(wc); | ||||
| #endif | ||||
| 	if (*width >= 0 && *width <= 0xff) | ||||
| 		return (UTF8_DONE); | ||||
| 	log_debug("UTF-8 %.*s, wcwidth() %d", (int)ud->size, ud->data, *width); | ||||
|   | ||||
| @@ -31,6 +31,7 @@ static struct screen	*window_buffer_init(struct window_mode_entry *, | ||||
| static void		 window_buffer_free(struct window_mode_entry *); | ||||
| static void		 window_buffer_resize(struct window_mode_entry *, u_int, | ||||
| 			     u_int); | ||||
| static void		 window_buffer_update(struct window_mode_entry *); | ||||
| static void		 window_buffer_key(struct window_mode_entry *, | ||||
| 			     struct client *, struct session *, | ||||
| 			     struct winlink *, key_code, struct mouse_event *); | ||||
| @@ -63,6 +64,7 @@ const struct window_mode window_buffer_mode = { | ||||
| 	.init = window_buffer_init, | ||||
| 	.free = window_buffer_free, | ||||
| 	.resize = window_buffer_resize, | ||||
| 	.update = window_buffer_update, | ||||
| 	.key = window_buffer_key, | ||||
| }; | ||||
|  | ||||
| @@ -335,6 +337,16 @@ window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy) | ||||
| 	mode_tree_resize(data->data, sx, sy); | ||||
| } | ||||
|  | ||||
| static void | ||||
| window_buffer_update(struct window_mode_entry *wme) | ||||
| { | ||||
| 	struct window_buffer_modedata	*data = wme->data; | ||||
|  | ||||
| 	mode_tree_build(data->data); | ||||
| 	mode_tree_draw(data->data); | ||||
| 	data->wp->flags |= PANE_REDRAW; | ||||
| } | ||||
|  | ||||
| static void | ||||
| window_buffer_do_delete(void *modedata, void *itemdata, | ||||
|     __unused struct client *c, __unused key_code key) | ||||
|   | ||||
| @@ -30,6 +30,7 @@ static struct screen	*window_client_init(struct window_mode_entry *, | ||||
| static void		 window_client_free(struct window_mode_entry *); | ||||
| static void		 window_client_resize(struct window_mode_entry *, u_int, | ||||
| 			     u_int); | ||||
| static void		 window_client_update(struct window_mode_entry *); | ||||
| static void		 window_client_key(struct window_mode_entry *, | ||||
| 			     struct client *, struct session *, | ||||
| 			     struct winlink *, key_code, struct mouse_event *); | ||||
| @@ -59,6 +60,7 @@ const struct window_mode window_client_mode = { | ||||
| 	.init = window_client_init, | ||||
| 	.free = window_client_free, | ||||
| 	.resize = window_client_resize, | ||||
| 	.update = window_client_update, | ||||
| 	.key = window_client_key, | ||||
| }; | ||||
|  | ||||
| @@ -311,6 +313,16 @@ window_client_resize(struct window_mode_entry *wme, u_int sx, u_int sy) | ||||
| 	mode_tree_resize(data->data, sx, sy); | ||||
| } | ||||
|  | ||||
| static void | ||||
| window_client_update(struct window_mode_entry *wme) | ||||
| { | ||||
| 	struct window_client_modedata	*data = wme->data; | ||||
|  | ||||
| 	mode_tree_build(data->data); | ||||
| 	mode_tree_draw(data->data); | ||||
| 	data->wp->flags |= PANE_REDRAW; | ||||
| } | ||||
|  | ||||
| static void | ||||
| window_client_do_detach(void *modedata, void *itemdata, | ||||
|     __unused struct client *c, key_code key) | ||||
|   | ||||
							
								
								
									
										763
									
								
								window-copy.c
									
									
									
									
									
								
							
							
						
						
									
										763
									
								
								window-copy.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -190,13 +190,6 @@ window_customize_scope_text(enum window_customize_scope scope, | ||||
| 	u_int	 idx; | ||||
|  | ||||
| 	switch (scope) { | ||||
| 	case WINDOW_CUSTOMIZE_NONE: | ||||
| 	case WINDOW_CUSTOMIZE_KEY: | ||||
| 	case WINDOW_CUSTOMIZE_SERVER: | ||||
| 	case WINDOW_CUSTOMIZE_GLOBAL_SESSION: | ||||
| 	case WINDOW_CUSTOMIZE_GLOBAL_WINDOW: | ||||
| 		s = xstrdup(""); | ||||
| 		break; | ||||
| 	case WINDOW_CUSTOMIZE_PANE: | ||||
| 		window_pane_index(fs->wp, &idx); | ||||
| 		xasprintf(&s, "pane %u", idx); | ||||
| @@ -207,6 +200,9 @@ window_customize_scope_text(enum window_customize_scope scope, | ||||
| 	case WINDOW_CUSTOMIZE_WINDOW: | ||||
| 		xasprintf(&s, "window %u", fs->wl->idx); | ||||
| 		break; | ||||
| 	default: | ||||
| 		s = xstrdup(""); | ||||
| 		break; | ||||
| 	} | ||||
| 	return (s); | ||||
| } | ||||
|   | ||||
| @@ -29,6 +29,7 @@ static struct screen	*window_tree_init(struct window_mode_entry *, | ||||
| static void		 window_tree_free(struct window_mode_entry *); | ||||
| static void		 window_tree_resize(struct window_mode_entry *, u_int, | ||||
| 			     u_int); | ||||
| static void		 window_tree_update(struct window_mode_entry *); | ||||
| static void		 window_tree_key(struct window_mode_entry *, | ||||
| 			     struct client *, struct session *, | ||||
| 			     struct winlink *, key_code, struct mouse_event *); | ||||
| @@ -79,6 +80,7 @@ const struct window_mode window_tree_mode = { | ||||
| 	.init = window_tree_init, | ||||
| 	.free = window_tree_free, | ||||
| 	.resize = window_tree_resize, | ||||
| 	.update = window_tree_update, | ||||
| 	.key = window_tree_key, | ||||
| }; | ||||
|  | ||||
| @@ -937,6 +939,16 @@ window_tree_resize(struct window_mode_entry *wme, u_int sx, u_int sy) | ||||
| 	mode_tree_resize(data->data, sx, sy); | ||||
| } | ||||
|  | ||||
| static void | ||||
| window_tree_update(struct window_mode_entry *wme) | ||||
| { | ||||
| 	struct window_tree_modedata	*data = wme->data; | ||||
|  | ||||
| 	mode_tree_build(data->data); | ||||
| 	mode_tree_draw(data->data); | ||||
| 	data->wp->flags |= PANE_REDRAW; | ||||
| } | ||||
|  | ||||
| static char * | ||||
| window_tree_get_target(struct window_tree_itemdata *item, | ||||
|     struct cmd_find_state *fs) | ||||
|   | ||||
							
								
								
									
										47
									
								
								window.c
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								window.c
									
									
									
									
									
								
							| @@ -61,17 +61,6 @@ static u_int	next_window_pane_id; | ||||
| static u_int	next_window_id; | ||||
| static u_int	next_active_point; | ||||
|  | ||||
| /* List of window modes. */ | ||||
| const struct window_mode *all_window_modes[] = { | ||||
| 	&window_buffer_mode, | ||||
| 	&window_client_mode, | ||||
| 	&window_clock_mode, | ||||
| 	&window_copy_mode, | ||||
| 	&window_tree_mode, | ||||
| 	&window_view_mode, | ||||
| 	NULL | ||||
| }; | ||||
|  | ||||
| struct window_pane_input_data { | ||||
| 	struct cmdq_item	*item; | ||||
| 	u_int			 wp; | ||||
| @@ -810,15 +799,18 @@ window_destroy_panes(struct window *w) | ||||
| } | ||||
|  | ||||
| const char * | ||||
| window_printable_flags(struct winlink *wl) | ||||
| window_printable_flags(struct winlink *wl, int escape) | ||||
| { | ||||
| 	struct session	*s = wl->session; | ||||
| 	static char	 flags[32]; | ||||
| 	int		 pos; | ||||
|  | ||||
| 	pos = 0; | ||||
| 	if (wl->flags & WINLINK_ACTIVITY) | ||||
| 	if (wl->flags & WINLINK_ACTIVITY) { | ||||
| 		flags[pos++] = '#'; | ||||
| 		if (escape) | ||||
| 			flags[pos++] = '#'; | ||||
| 	} | ||||
| 	if (wl->flags & WINLINK_BELL) | ||||
| 		flags[pos++] = '!'; | ||||
| 	if (wl->flags & WINLINK_SILENCE) | ||||
| @@ -1155,12 +1147,27 @@ window_pane_reset_mode_all(struct window_pane *wp) | ||||
| 		window_pane_reset_mode(wp); | ||||
| } | ||||
|  | ||||
| static void | ||||
| window_pane_copy_key(struct window_pane *wp, key_code key) | ||||
| { | ||||
|  	struct window_pane	*loop; | ||||
|  | ||||
| 	TAILQ_FOREACH(loop, &wp->window->panes, entry) { | ||||
| 		if (loop != wp && | ||||
| 		    TAILQ_EMPTY(&loop->modes) && | ||||
| 		    loop->fd != -1 && | ||||
| 		    (~loop->flags & PANE_INPUTOFF) && | ||||
| 		    window_pane_visible(loop) && | ||||
| 		    options_get_number(loop->options, "synchronize-panes")) | ||||
| 			input_key_pane(loop, key, NULL); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int | ||||
| window_pane_key(struct window_pane *wp, struct client *c, struct session *s, | ||||
|     struct winlink *wl, key_code key, struct mouse_event *m) | ||||
| { | ||||
| 	struct window_mode_entry	*wme; | ||||
| 	struct window_pane		*wp2; | ||||
|  | ||||
| 	if (KEYC_IS_MOUSE(key) && m == NULL) | ||||
| 		return (-1); | ||||
| @@ -1182,16 +1189,8 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s, | ||||
|  | ||||
| 	if (KEYC_IS_MOUSE(key)) | ||||
| 		return (0); | ||||
| 	if (options_get_number(wp->window->options, "synchronize-panes")) { | ||||
| 		TAILQ_FOREACH(wp2, &wp->window->panes, entry) { | ||||
| 			if (wp2 != wp && | ||||
| 			    TAILQ_EMPTY(&wp2->modes) && | ||||
| 			    wp2->fd != -1 && | ||||
| 			    (~wp2->flags & PANE_INPUTOFF) && | ||||
| 			    window_pane_visible(wp2)) | ||||
| 				input_key_pane(wp2, key, NULL); | ||||
| 		} | ||||
| 	} | ||||
| 	if (options_get_number(wp->options, "synchronize-panes")) | ||||
| 		window_pane_copy_key(wp, key); | ||||
| 	return (0); | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Nicholas Marriott
					Nicholas Marriott