xref: /openbsd-src/usr.bin/tmux/paste.c (revision a41fa27a7a7d81a2718aed810231593ca5d4d42a)
1 /* $OpenBSD: paste.c,v 1.19 2014/05/13 07:34:35 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicm@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 #include <sys/time.h>
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <vis.h>
25 
26 #include "tmux.h"
27 
28 /*
29  * Set of paste buffers. Note that paste buffer data is not necessarily a C
30  * string!
31  */
32 
33 u_int	paste_next_index;
34 u_int	paste_next_order;
35 u_int	paste_num_automatic;
36 RB_HEAD(paste_name_tree, paste_buffer) paste_by_name;
37 RB_HEAD(paste_time_tree, paste_buffer) paste_by_time;
38 
39 int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *);
40 RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
41 RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
42 
43 int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *);
44 RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
45 RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
46 
47 int
48 paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b)
49 {
50 	return (strcmp(a->name, b->name));
51 }
52 
53 int
54 paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b)
55 {
56 	if (a->order > b->order)
57 		return (-1);
58 	if (a->order < b->order)
59 		return (1);
60 	return (0);
61 }
62 
63 /* Walk paste buffers by name. */
64 struct paste_buffer *
65 paste_walk(struct paste_buffer *pb)
66 {
67 	if (pb == NULL)
68 		return (RB_MIN(paste_time_tree, &paste_by_time));
69 	return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
70 }
71 
72 /* Get the most recent automatic buffer */
73 struct paste_buffer *
74 paste_get_top(void)
75 {
76 	struct paste_buffer	*pb;
77 
78 	pb = RB_MIN(paste_time_tree, &paste_by_time);
79 	if (pb == NULL)
80 		return (NULL);
81 	return (pb);
82 }
83 
84 /* Free the most recent buffer */
85 int
86 paste_free_top(void)
87 {
88 	struct paste_buffer	*pb;
89 
90 	pb = paste_get_top();
91 	if (pb == NULL)
92 		return (-1);
93 	return (paste_free_name(pb->name));
94 }
95 
96 /* Get a paste buffer by name. */
97 struct paste_buffer *
98 paste_get_name(const char *name)
99 {
100 	struct paste_buffer	pbfind;
101 
102 	if (name == NULL || *name == '\0')
103 		return (NULL);
104 
105 	pbfind.name = (char*)name;
106 	return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
107 }
108 
109 /* Free a paste buffer by name. */
110 int
111 paste_free_name(const char *name)
112 {
113 	struct paste_buffer	*pb, pbfind;
114 
115 	if (name == NULL || *name == '\0')
116 		return (-1);
117 
118 	pbfind.name = (char*)name;
119 	pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind);
120 	if (pb == NULL)
121 		return (-1);
122 
123 	RB_REMOVE(paste_name_tree, &paste_by_name, pb);
124 	RB_REMOVE(paste_time_tree, &paste_by_time, pb);
125 	if (pb->automatic)
126 		paste_num_automatic--;
127 
128 	free(pb->data);
129 	free(pb->name);
130 	free(pb);
131 	return (0);
132 }
133 
134 /*
135  * Add an automatic buffer, freeing the oldest automatic item if at limit. Note
136  * that the caller is responsible for allocating data.
137  */
138 void
139 paste_add(char *data, size_t size)
140 {
141 	struct paste_buffer	*pb, *pb1;
142 	u_int			 limit;
143 
144 	if (size == 0)
145 		return;
146 
147 	limit = options_get_number(&global_options, "buffer-limit");
148 	RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
149 		if (paste_num_automatic < limit)
150 			break;
151 		if (pb->automatic)
152 			paste_free_name(pb->name);
153 	}
154 
155 	pb = xmalloc(sizeof *pb);
156 
157 	pb->name = NULL;
158 	do {
159 		free(pb->name);
160 		xasprintf(&pb->name, "buffer%04u", paste_next_index);
161 		paste_next_index++;
162 	} while (paste_get_name(pb->name) != NULL);
163 
164 	pb->data = data;
165 	pb->size = size;
166 
167 	pb->automatic = 1;
168 	paste_num_automatic++;
169 
170 	pb->order = paste_next_order++;
171 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
172 	RB_INSERT(paste_time_tree, &paste_by_time, pb);
173 }
174 
175 /* Rename a paste buffer. */
176 int
177 paste_rename(const char *oldname, const char *newname, char **cause)
178 {
179 	struct paste_buffer	*pb;
180 
181 	if (cause != NULL)
182 		*cause = NULL;
183 
184 	if (oldname == NULL || *oldname == '\0') {
185 		if (cause != NULL)
186 			*cause = xstrdup("no buffer");
187 		return (-1);
188 	}
189 	if (newname == NULL || *newname == '\0') {
190 		if (cause != NULL)
191 			*cause = xstrdup("new name is empty");
192 		return (-1);
193 	}
194 
195 	pb = paste_get_name(oldname);
196 	if (pb == NULL) {
197 		if (cause != NULL)
198 		    xasprintf(cause, "no buffer %s", oldname);
199 		return (-1);
200 	}
201 
202 	RB_REMOVE(paste_name_tree, &paste_by_name, pb);
203 
204 	free(pb->name);
205 	pb->name = xstrdup(newname);
206 
207 	if (pb->automatic)
208 		paste_num_automatic--;
209 	pb->automatic = 0;
210 
211 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
212 
213 	return (0);
214 }
215 
216 /*
217  * Add or replace an item in the store. Note that the caller is responsible for
218  * allocating data.
219  */
220 int
221 paste_set(char *data, size_t size, const char *name, char **cause)
222 {
223 	struct paste_buffer	*pb;
224 
225 	if (cause != NULL)
226 		*cause = NULL;
227 
228 	if (size == 0) {
229 		free(data);
230 		return (0);
231 	}
232 	if (name == NULL) {
233 		paste_add(data, size);
234 		return (0);
235 	}
236 
237 	if (*name == '\0') {
238 		if (cause != NULL)
239 			*cause = xstrdup("empty buffer name");
240 		return (-1);
241 	}
242 
243 	pb = paste_get_name(name);
244 	if (pb != NULL)
245 		paste_free_name(name);
246 
247 	pb = xmalloc(sizeof *pb);
248 
249 	pb->name = xstrdup(name);
250 
251 	pb->data = data;
252 	pb->size = size;
253 
254 	pb->automatic = 0;
255 	pb->order = paste_next_order++;
256 
257 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
258 	RB_INSERT(paste_time_tree, &paste_by_time, pb);
259 
260 	return (0);
261 }
262 
263 /* Convert start of buffer into a nice string. */
264 char *
265 paste_make_sample(struct paste_buffer *pb, int utf8flag)
266 {
267 	char		*buf;
268 	size_t		 len, used;
269 	const int	 flags = VIS_OCTAL|VIS_TAB|VIS_NL;
270 	const size_t	 width = 200;
271 
272 	len = pb->size;
273 	if (len > width)
274 		len = width;
275 	buf = xmalloc(len * 4 + 4);
276 
277 	if (utf8flag)
278 		used = utf8_strvis(buf, pb->data, len, flags);
279 	else
280 		used = strvisx(buf, pb->data, len, flags);
281 	if (pb->size > width || used > width)
282 		strlcpy(buf + width, "...", 4);
283 	return (buf);
284 }
285 
286 /* Paste into a window pane, filtering '\n' according to separator. */
287 void
288 paste_send_pane(struct paste_buffer *pb, struct window_pane *wp,
289     const char *sep, int bracket)
290 {
291 	const char	*data = pb->data, *end = data + pb->size, *lf;
292 	size_t		 seplen;
293 
294 	if (bracket)
295 		bufferevent_write(wp->event, "\033[200~", 6);
296 
297 	seplen = strlen(sep);
298 	while ((lf = memchr(data, '\n', end - data)) != NULL) {
299 		if (lf != data)
300 			bufferevent_write(wp->event, data, lf - data);
301 		bufferevent_write(wp->event, sep, seplen);
302 		data = lf + 1;
303 	}
304 
305 	if (end != data)
306 		bufferevent_write(wp->event, data, end - data);
307 
308 	if (bracket)
309 		bufferevent_write(wp->event, "\033[201~", 6);
310 }
311