xref: /openbsd-src/usr.bin/tmux/paste.c (revision a54272040a00a8b56dfcc10e800ecb7434168426)
1 /* $OpenBSD: paste.c,v 1.21 2014/06/20 11:00:19 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, *pb_new;
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 	pb_new = paste_get_name(newname);
203 	if (pb_new != NULL) {
204 		if (cause != NULL)
205 			xasprintf(cause, "buffer %s already exists", newname);
206 		return (-1);
207 	}
208 
209 	RB_REMOVE(paste_name_tree, &paste_by_name, pb);
210 
211 	free(pb->name);
212 	pb->name = xstrdup(newname);
213 
214 	if (pb->automatic)
215 		paste_num_automatic--;
216 	pb->automatic = 0;
217 
218 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
219 
220 	return (0);
221 }
222 
223 /*
224  * Add or replace an item in the store. Note that the caller is responsible for
225  * allocating data.
226  */
227 int
228 paste_set(char *data, size_t size, const char *name, char **cause)
229 {
230 	struct paste_buffer	*pb;
231 
232 	if (cause != NULL)
233 		*cause = NULL;
234 
235 	if (size == 0) {
236 		free(data);
237 		return (0);
238 	}
239 	if (name == NULL) {
240 		paste_add(data, size);
241 		return (0);
242 	}
243 
244 	if (*name == '\0') {
245 		if (cause != NULL)
246 			*cause = xstrdup("empty buffer name");
247 		return (-1);
248 	}
249 
250 	pb = paste_get_name(name);
251 	if (pb != NULL)
252 		paste_free_name(name);
253 
254 	pb = xmalloc(sizeof *pb);
255 
256 	pb->name = xstrdup(name);
257 
258 	pb->data = data;
259 	pb->size = size;
260 
261 	pb->automatic = 0;
262 	pb->order = paste_next_order++;
263 
264 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
265 	RB_INSERT(paste_time_tree, &paste_by_time, pb);
266 
267 	return (0);
268 }
269 
270 /* Convert start of buffer into a nice string. */
271 char *
272 paste_make_sample(struct paste_buffer *pb, int utf8flag)
273 {
274 	char		*buf;
275 	size_t		 len, used;
276 	const int	 flags = VIS_OCTAL|VIS_TAB|VIS_NL;
277 	const size_t	 width = 200;
278 
279 	len = pb->size;
280 	if (len > width)
281 		len = width;
282 	buf = xmalloc(len * 4 + 4);
283 
284 	if (utf8flag)
285 		used = utf8_strvis(buf, pb->data, len, flags);
286 	else
287 		used = strvisx(buf, pb->data, len, flags);
288 	if (pb->size > width || used > width)
289 		strlcpy(buf + width, "...", 4);
290 	return (buf);
291 }
292 
293 /* Paste into a window pane, filtering '\n' according to separator. */
294 void
295 paste_send_pane(struct paste_buffer *pb, struct window_pane *wp,
296     const char *sep, int bracket)
297 {
298 	const char	*data = pb->data, *end = data + pb->size, *lf;
299 	size_t		 seplen;
300 
301 	if (bracket)
302 		bufferevent_write(wp->event, "\033[200~", 6);
303 
304 	seplen = strlen(sep);
305 	while ((lf = memchr(data, '\n', end - data)) != NULL) {
306 		if (lf != data)
307 			bufferevent_write(wp->event, data, lf - data);
308 		bufferevent_write(wp->event, sep, seplen);
309 		data = lf + 1;
310 	}
311 
312 	if (end != data)
313 		bufferevent_write(wp->event, data, end - data);
314 
315 	if (bracket)
316 		bufferevent_write(wp->event, "\033[201~", 6);
317 }
318