xref: /netbsd-src/external/bsd/tmux/dist/cmd-capture-pane.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2009 Jonathan Alvarado <radobobo@users.sourceforge.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "tmux.h"
25 
26 /*
27  * Write the entire contents of a pane to a buffer or stdout.
28  */
29 
30 enum cmd_retval	 cmd_capture_pane_exec(struct cmd *, struct cmd_q *);
31 
32 char		*cmd_capture_pane_append(char *, size_t *, char *, size_t);
33 char		*cmd_capture_pane_pending(struct args *, struct window_pane *,
34 		     size_t *);
35 char		*cmd_capture_pane_history(struct args *, struct cmd_q *,
36 		     struct window_pane *, size_t *);
37 
38 const struct cmd_entry cmd_capture_pane_entry = {
39 	"capture-pane", "capturep",
40 	"ab:CeE:JpPqS:t:", 0, 0,
41 	"[-aCeJpPq] " CMD_BUFFER_USAGE " [-E end-line] [-S start-line]"
42 	CMD_TARGET_PANE_USAGE,
43 	0,
44 	cmd_capture_pane_exec
45 };
46 
47 char *
48 cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen)
49 {
50 	buf = xrealloc(buf, *len + linelen + 1);
51 	memcpy(buf + *len, line, linelen);
52 	*len += linelen;
53 	return (buf);
54 }
55 
56 char *
57 cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
58     size_t *len)
59 {
60 	struct evbuffer	*pending;
61 	char		*buf, *line, tmp[5];
62 	size_t		 linelen;
63 	u_int		 i;
64 
65 	pending = input_pending(wp);
66 	if (pending == NULL)
67 		return (xstrdup(""));
68 
69 	line = (char *)EVBUFFER_DATA(pending);
70 	linelen = EVBUFFER_LENGTH(pending);
71 
72 	buf = xstrdup("");
73 	if (args_has(args, 'C')) {
74 		for (i = 0; i < linelen; i++) {
75 			if (line[i] >= ' ') {
76 				tmp[0] = line[i];
77 				tmp[1] = '\0';
78 			} else
79 				xsnprintf(tmp, sizeof tmp, "\\%03hho", line[i]);
80 			buf = cmd_capture_pane_append(buf, len, tmp,
81 			    strlen(tmp));
82 		}
83 	} else
84 		buf = cmd_capture_pane_append(buf, len, line, linelen);
85 	return (buf);
86 }
87 
88 char *
89 cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq,
90     struct window_pane *wp, size_t *len)
91 {
92 	struct grid		*gd;
93 	const struct grid_line	*gl;
94 	struct grid_cell	*gc = NULL;
95 	int			 n, with_codes, escape_c0, join_lines;
96 	u_int			 i, sx, top, bottom, tmp;
97 	char			*cause, *buf, *line;
98 	const char		*Sflag, *Eflag;
99 	size_t			 linelen;
100 
101 	sx = screen_size_x(&wp->base);
102 	if (args_has(args, 'a')) {
103 		gd = wp->saved_grid;
104 		if (gd == NULL) {
105 			if (!args_has(args, 'q')) {
106 				cmdq_error(cmdq, "no alternate screen");
107 				return (NULL);
108 			}
109 			return (xstrdup(""));
110 		}
111 	} else
112 		gd = wp->base.grid;
113 
114 	Sflag = args_get(args, 'S');
115 	if (Sflag != NULL && strcmp(Sflag, "-") == 0)
116 		top = 0;
117 	else {
118 		n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause);
119 		if (cause != NULL) {
120 			top = gd->hsize;
121 			free(cause);
122 		} else if (n < 0 && (u_int) -n > gd->hsize)
123 			top = 0;
124 		else
125 			top = gd->hsize + n;
126 		if (top > gd->hsize + gd->sy - 1)
127 			top = gd->hsize + gd->sy - 1;
128 	}
129 
130 	Eflag = args_get(args, 'E');
131 	if (Eflag != NULL && strcmp(Eflag, "-") == 0)
132 		bottom = gd->hsize + gd->sy - 1;
133 	else {
134 		n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause);
135 		if (cause != NULL) {
136 			bottom = gd->hsize + gd->sy - 1;
137 			free(cause);
138 		} else if (n < 0 && (u_int) -n > gd->hsize)
139 			bottom = 0;
140 		else
141 			bottom = gd->hsize + n;
142 		if (bottom > gd->hsize + gd->sy - 1)
143 			bottom = gd->hsize + gd->sy - 1;
144 	}
145 
146 	if (bottom < top) {
147 		tmp = bottom;
148 		bottom = top;
149 		top = tmp;
150 	}
151 
152 	with_codes = args_has(args, 'e');
153 	escape_c0 = args_has(args, 'C');
154 	join_lines = args_has(args, 'J');
155 
156 	buf = NULL;
157 	for (i = top; i <= bottom; i++) {
158 		line = grid_string_cells(gd, 0, i, sx, &gc, with_codes,
159 		    escape_c0, !join_lines);
160 		linelen = strlen(line);
161 
162 		buf = cmd_capture_pane_append(buf, len, line, linelen);
163 
164 		gl = grid_peek_line(gd, i);
165 		if (!join_lines || !(gl->flags & GRID_LINE_WRAPPED))
166 			buf[(*len)++] = '\n';
167 
168 		free(line);
169 	}
170 	return (buf);
171 }
172 
173 enum cmd_retval
174 cmd_capture_pane_exec(struct cmd *self, struct cmd_q *cmdq)
175 {
176 	struct args		*args = self->args;
177 	struct client		*c;
178 	struct window_pane	*wp;
179 	char			*buf, *cause;
180 	const char		*bufname;
181 	size_t			 len;
182 
183 	if (cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp) == NULL)
184 		return (CMD_RETURN_ERROR);
185 
186 	len = 0;
187 	if (args_has(args, 'P'))
188 		buf = cmd_capture_pane_pending(args, wp, &len);
189 	else
190 		buf = cmd_capture_pane_history(args, cmdq, wp, &len);
191 	if (buf == NULL)
192 		return (CMD_RETURN_ERROR);
193 
194 	if (args_has(args, 'p')) {
195 		c = cmdq->client;
196 		if (c == NULL ||
197 		    (c->session != NULL && !(c->flags & CLIENT_CONTROL))) {
198 			cmdq_error(cmdq, "can't write to stdout");
199 			free(buf);
200 			return (CMD_RETURN_ERROR);
201 		}
202 		evbuffer_add(c->stdout_data, buf, len);
203 		free(buf);
204 		if (args_has(args, 'P') && len > 0)
205 		    evbuffer_add(c->stdout_data, "\n", 1);
206 		server_push_stdout(c);
207 	} else {
208 		bufname = NULL;
209 		if (args_has(args, 'b'))
210 			bufname = args_get(args, 'b');
211 
212 		if (paste_set(buf, len, bufname, &cause) != 0) {
213 			cmdq_error(cmdq, "%s", cause);
214 			free(cause);
215 			free(buf);
216 			return (CMD_RETURN_ERROR);
217 		}
218 	}
219 
220 	return (CMD_RETURN_NORMAL);
221 }
222