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