xref: /netbsd-src/external/bsd/tmux/dist/paste.c (revision f844e94ef29eebc7999c12636b87f541bb86868b)
15494e770Schristos /* $OpenBSD$ */
2698d5317Sjmmv 
3698d5317Sjmmv /*
4f26e8bc9Schristos  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5698d5317Sjmmv  *
6698d5317Sjmmv  * Permission to use, copy, modify, and distribute this software for any
7698d5317Sjmmv  * purpose with or without fee is hereby granted, provided that the above
8698d5317Sjmmv  * copyright notice and this permission notice appear in all copies.
9698d5317Sjmmv  *
10698d5317Sjmmv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11698d5317Sjmmv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12698d5317Sjmmv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13698d5317Sjmmv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14698d5317Sjmmv  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15698d5317Sjmmv  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16698d5317Sjmmv  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17698d5317Sjmmv  */
18698d5317Sjmmv 
19698d5317Sjmmv #include <sys/types.h>
20698d5317Sjmmv 
21928fc495Schristos #include <stdlib.h>
22698d5317Sjmmv #include <string.h>
23e9a2d6faSchristos #include <time.h>
24698d5317Sjmmv 
25698d5317Sjmmv #include "tmux.h"
26698d5317Sjmmv 
27698d5317Sjmmv /*
285494e770Schristos  * Set of paste buffers. Note that paste buffer data is not necessarily a C
29698d5317Sjmmv  * string!
30698d5317Sjmmv  */
31698d5317Sjmmv 
325494e770Schristos struct paste_buffer {
335494e770Schristos 	char		*data;
345494e770Schristos 	size_t		 size;
355494e770Schristos 
365494e770Schristos 	char		*name;
37e9a2d6faSchristos 	time_t		 created;
385494e770Schristos 	int		 automatic;
395494e770Schristos 	u_int		 order;
405494e770Schristos 
415494e770Schristos 	RB_ENTRY(paste_buffer) name_entry;
425494e770Schristos 	RB_ENTRY(paste_buffer) time_entry;
435494e770Schristos };
445494e770Schristos 
45e9a2d6faSchristos static u_int	paste_next_index;
46e9a2d6faSchristos static u_int	paste_next_order;
47e9a2d6faSchristos static u_int	paste_num_automatic;
48e9a2d6faSchristos static RB_HEAD(paste_name_tree, paste_buffer) paste_by_name;
49e9a2d6faSchristos static RB_HEAD(paste_time_tree, paste_buffer) paste_by_time;
505494e770Schristos 
51e9a2d6faSchristos static int	paste_cmp_names(const struct paste_buffer *,
52e9a2d6faSchristos 		    const struct paste_buffer *);
53e9a2d6faSchristos RB_GENERATE_STATIC(paste_name_tree, paste_buffer, name_entry, paste_cmp_names);
545494e770Schristos 
55e9a2d6faSchristos static int	paste_cmp_times(const struct paste_buffer *,
56e9a2d6faSchristos 		    const struct paste_buffer *);
57e9a2d6faSchristos RB_GENERATE_STATIC(paste_time_tree, paste_buffer, time_entry, paste_cmp_times);
585494e770Schristos 
59e9a2d6faSchristos static int
paste_cmp_names(const struct paste_buffer * a,const struct paste_buffer * b)605494e770Schristos paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b)
615494e770Schristos {
625494e770Schristos 	return (strcmp(a->name, b->name));
635494e770Schristos }
645494e770Schristos 
65e9a2d6faSchristos static int
paste_cmp_times(const struct paste_buffer * a,const struct paste_buffer * b)665494e770Schristos paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b)
675494e770Schristos {
685494e770Schristos 	if (a->order > b->order)
695494e770Schristos 		return (-1);
705494e770Schristos 	if (a->order < b->order)
715494e770Schristos 		return (1);
725494e770Schristos 	return (0);
735494e770Schristos }
745494e770Schristos 
755494e770Schristos /* Get paste buffer name. */
765494e770Schristos const char *
paste_buffer_name(struct paste_buffer * pb)775494e770Schristos paste_buffer_name(struct paste_buffer *pb)
785494e770Schristos {
795494e770Schristos 	return (pb->name);
805494e770Schristos }
815494e770Schristos 
82e9a2d6faSchristos /* Get paste buffer order. */
83e9a2d6faSchristos u_int
paste_buffer_order(struct paste_buffer * pb)84e9a2d6faSchristos paste_buffer_order(struct paste_buffer *pb)
85e9a2d6faSchristos {
86e9a2d6faSchristos 	return (pb->order);
87e9a2d6faSchristos }
88e9a2d6faSchristos 
89e9a2d6faSchristos /* Get paste buffer created. */
90e9a2d6faSchristos time_t
paste_buffer_created(struct paste_buffer * pb)91e9a2d6faSchristos paste_buffer_created(struct paste_buffer *pb)
92e9a2d6faSchristos {
93e9a2d6faSchristos 	return (pb->created);
94e9a2d6faSchristos }
95e9a2d6faSchristos 
965494e770Schristos /* Get paste buffer data. */
975494e770Schristos const char *
paste_buffer_data(struct paste_buffer * pb,size_t * size)985494e770Schristos paste_buffer_data(struct paste_buffer *pb, size_t *size)
995494e770Schristos {
1005494e770Schristos 	if (size != NULL)
1015494e770Schristos 		*size = pb->size;
1025494e770Schristos 	return (pb->data);
1035494e770Schristos }
1045494e770Schristos 
105e9a2d6faSchristos /* Walk paste buffers by time. */
106698d5317Sjmmv struct paste_buffer *
paste_walk(struct paste_buffer * pb)1075494e770Schristos paste_walk(struct paste_buffer *pb)
1085494e770Schristos {
1095494e770Schristos 	if (pb == NULL)
1105494e770Schristos 		return (RB_MIN(paste_time_tree, &paste_by_time));
1115494e770Schristos 	return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
1125494e770Schristos }
1135494e770Schristos 
114*f844e94eSwiz int
paste_is_empty(void)115*f844e94eSwiz paste_is_empty(void)
116*f844e94eSwiz {
117*f844e94eSwiz 	return RB_ROOT(&paste_by_time) == NULL;
118*f844e94eSwiz }
119*f844e94eSwiz 
1205494e770Schristos /* Get the most recent automatic buffer. */
1215494e770Schristos struct paste_buffer *
paste_get_top(const char ** name)1225494e770Schristos paste_get_top(const char **name)
123698d5317Sjmmv {
124698d5317Sjmmv 	struct paste_buffer	*pb;
125698d5317Sjmmv 
1265494e770Schristos 	pb = RB_MIN(paste_time_tree, &paste_by_time);
127*f844e94eSwiz 	while (pb != NULL && !pb->automatic)
128*f844e94eSwiz 		pb = RB_NEXT(paste_time_tree, &paste_by_time, pb);
1295494e770Schristos 	if (pb == NULL)
1305494e770Schristos 		return (NULL);
1315494e770Schristos 	if (name != NULL)
1325494e770Schristos 		*name = pb->name;
133698d5317Sjmmv 	return (pb);
134698d5317Sjmmv }
135698d5317Sjmmv 
1365494e770Schristos /* Get a paste buffer by name. */
137698d5317Sjmmv struct paste_buffer *
paste_get_name(const char * name)1385494e770Schristos paste_get_name(const char *name)
139698d5317Sjmmv {
1405494e770Schristos 	struct paste_buffer	pbfind;
1415494e770Schristos 
1425494e770Schristos 	if (name == NULL || *name == '\0')
143698d5317Sjmmv 		return (NULL);
1445494e770Schristos 
14599e242abSchristos 	pbfind.name = __UNCONST(name);
1465494e770Schristos 	return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind));
147698d5317Sjmmv }
148698d5317Sjmmv 
1495494e770Schristos /* Free a paste buffer. */
1505494e770Schristos void
paste_free(struct paste_buffer * pb)1515494e770Schristos paste_free(struct paste_buffer *pb)
152698d5317Sjmmv {
153*f844e94eSwiz 	notify_paste_buffer(pb->name, 1);
154*f844e94eSwiz 
1555494e770Schristos 	RB_REMOVE(paste_name_tree, &paste_by_name, pb);
1565494e770Schristos 	RB_REMOVE(paste_time_tree, &paste_by_time, pb);
1575494e770Schristos 	if (pb->automatic)
1585494e770Schristos 		paste_num_automatic--;
159698d5317Sjmmv 
160928fc495Schristos 	free(pb->data);
1615494e770Schristos 	free(pb->name);
162928fc495Schristos 	free(pb);
163698d5317Sjmmv }
164698d5317Sjmmv 
165698d5317Sjmmv /*
1665494e770Schristos  * Add an automatic buffer, freeing the oldest automatic item if at limit. Note
167698d5317Sjmmv  * that the caller is responsible for allocating data.
168698d5317Sjmmv  */
169698d5317Sjmmv void
paste_add(const char * prefix,char * data,size_t size)17030744affSchristos paste_add(const char *prefix, char *data, size_t size)
171698d5317Sjmmv {
1725494e770Schristos 	struct paste_buffer	*pb, *pb1;
1735494e770Schristos 	u_int			 limit;
174698d5317Sjmmv 
17530744affSchristos 	if (prefix == NULL)
17630744affSchristos 		prefix = "buffer";
17730744affSchristos 
178e9a2d6faSchristos 	if (size == 0) {
179e9a2d6faSchristos 		free(data);
180698d5317Sjmmv 		return;
181e9a2d6faSchristos 	}
182698d5317Sjmmv 
183f26e8bc9Schristos 	limit = options_get_number(global_options, "buffer-limit");
1845494e770Schristos 	RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) {
1855494e770Schristos 		if (paste_num_automatic < limit)
1865494e770Schristos 			break;
1875494e770Schristos 		if (pb->automatic)
1885494e770Schristos 			paste_free(pb);
189698d5317Sjmmv 	}
190698d5317Sjmmv 
191698d5317Sjmmv 	pb = xmalloc(sizeof *pb);
1925494e770Schristos 
1935494e770Schristos 	pb->name = NULL;
1945494e770Schristos 	do {
1955494e770Schristos 		free(pb->name);
19630744affSchristos 		xasprintf(&pb->name, "%s%u", prefix, paste_next_index);
1975494e770Schristos 		paste_next_index++;
1985494e770Schristos 	} while (paste_get_name(pb->name) != NULL);
199698d5317Sjmmv 
200698d5317Sjmmv 	pb->data = data;
201698d5317Sjmmv 	pb->size = size;
2025494e770Schristos 
2035494e770Schristos 	pb->automatic = 1;
2045494e770Schristos 	paste_num_automatic++;
2055494e770Schristos 
206e9a2d6faSchristos 	pb->created = time(NULL);
207e9a2d6faSchristos 
2085494e770Schristos 	pb->order = paste_next_order++;
2095494e770Schristos 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
2105494e770Schristos 	RB_INSERT(paste_time_tree, &paste_by_time, pb);
211*f844e94eSwiz 
212*f844e94eSwiz 	notify_paste_buffer(pb->name, 0);
213698d5317Sjmmv }
214698d5317Sjmmv 
2155494e770Schristos /* Rename a paste buffer. */
2165494e770Schristos int
paste_rename(const char * oldname,const char * newname,char ** cause)2175494e770Schristos paste_rename(const char *oldname, const char *newname, char **cause)
2185494e770Schristos {
2195494e770Schristos 	struct paste_buffer	*pb, *pb_new;
2205494e770Schristos 
2215494e770Schristos 	if (cause != NULL)
2225494e770Schristos 		*cause = NULL;
2235494e770Schristos 
2245494e770Schristos 	if (oldname == NULL || *oldname == '\0') {
2255494e770Schristos 		if (cause != NULL)
2265494e770Schristos 			*cause = xstrdup("no buffer");
2275494e770Schristos 		return (-1);
2285494e770Schristos 	}
2295494e770Schristos 	if (newname == NULL || *newname == '\0') {
2305494e770Schristos 		if (cause != NULL)
2315494e770Schristos 			*cause = xstrdup("new name is empty");
2325494e770Schristos 		return (-1);
2335494e770Schristos 	}
2345494e770Schristos 
2355494e770Schristos 	pb = paste_get_name(oldname);
2365494e770Schristos 	if (pb == NULL) {
2375494e770Schristos 		if (cause != NULL)
2385494e770Schristos 			xasprintf(cause, "no buffer %s", oldname);
2395494e770Schristos 		return (-1);
2405494e770Schristos 	}
2415494e770Schristos 
2425494e770Schristos 	pb_new = paste_get_name(newname);
243*f844e94eSwiz 	if (pb_new != NULL)
244*f844e94eSwiz 		paste_free(pb_new);
2455494e770Schristos 
2465494e770Schristos 	RB_REMOVE(paste_name_tree, &paste_by_name, pb);
2475494e770Schristos 
2485494e770Schristos 	free(pb->name);
2495494e770Schristos 	pb->name = xstrdup(newname);
2505494e770Schristos 
2515494e770Schristos 	if (pb->automatic)
2525494e770Schristos 		paste_num_automatic--;
2535494e770Schristos 	pb->automatic = 0;
2545494e770Schristos 
2555494e770Schristos 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
2565494e770Schristos 
257*f844e94eSwiz 	notify_paste_buffer(oldname, 1);
258*f844e94eSwiz 	notify_paste_buffer(newname, 0);
259*f844e94eSwiz 
2605494e770Schristos 	return (0);
2615494e770Schristos }
262698d5317Sjmmv 
263698d5317Sjmmv /*
2645494e770Schristos  * Add or replace an item in the store. Note that the caller is responsible for
265698d5317Sjmmv  * allocating data.
266698d5317Sjmmv  */
267698d5317Sjmmv int
paste_set(char * data,size_t size,const char * name,char ** cause)2685494e770Schristos paste_set(char *data, size_t size, const char *name, char **cause)
269698d5317Sjmmv {
2705494e770Schristos 	struct paste_buffer	*pb, *old;
2715494e770Schristos 
2725494e770Schristos 	if (cause != NULL)
2735494e770Schristos 		*cause = NULL;
274698d5317Sjmmv 
275928fc495Schristos 	if (size == 0) {
276928fc495Schristos 		free(data);
277698d5317Sjmmv 		return (0);
278928fc495Schristos 	}
2795494e770Schristos 	if (name == NULL) {
28030744affSchristos 		paste_add(NULL, data, size);
2815494e770Schristos 		return (0);
2825494e770Schristos 	}
283698d5317Sjmmv 
2845494e770Schristos 	if (*name == '\0') {
2855494e770Schristos 		if (cause != NULL)
2865494e770Schristos 			*cause = xstrdup("empty buffer name");
287698d5317Sjmmv 		return (-1);
2885494e770Schristos 	}
289698d5317Sjmmv 
2905494e770Schristos 	pb = xmalloc(sizeof *pb);
2915494e770Schristos 
2925494e770Schristos 	pb->name = xstrdup(name);
293698d5317Sjmmv 
294698d5317Sjmmv 	pb->data = data;
295698d5317Sjmmv 	pb->size = size;
296698d5317Sjmmv 
2975494e770Schristos 	pb->automatic = 0;
2985494e770Schristos 	pb->order = paste_next_order++;
2995494e770Schristos 
300e9a2d6faSchristos 	pb->created = time(NULL);
301e9a2d6faSchristos 
3025494e770Schristos 	if ((old = paste_get_name(name)) != NULL)
3035494e770Schristos 		paste_free(old);
3045494e770Schristos 
3055494e770Schristos 	RB_INSERT(paste_name_tree, &paste_by_name, pb);
3065494e770Schristos 	RB_INSERT(paste_time_tree, &paste_by_time, pb);
3075494e770Schristos 
308*f844e94eSwiz 	notify_paste_buffer(name, 0);
309*f844e94eSwiz 
310698d5317Sjmmv 	return (0);
311698d5317Sjmmv }
312698d5317Sjmmv 
313e271dbb8Schristos /* Set paste data without otherwise changing it. */
314e271dbb8Schristos void
paste_replace(struct paste_buffer * pb,char * data,size_t size)315e271dbb8Schristos paste_replace(struct paste_buffer *pb, char *data, size_t size)
316e271dbb8Schristos {
317e271dbb8Schristos 	free(pb->data);
318e271dbb8Schristos 	pb->data = data;
319e271dbb8Schristos 	pb->size = size;
320*f844e94eSwiz 
321*f844e94eSwiz 	notify_paste_buffer(pb->name, 0);
322e271dbb8Schristos }
323e271dbb8Schristos 
3245494e770Schristos /* Convert start of buffer into a nice string. */
325698d5317Sjmmv char *
paste_make_sample(struct paste_buffer * pb)326f26e8bc9Schristos paste_make_sample(struct paste_buffer *pb)
327698d5317Sjmmv {
328698d5317Sjmmv 	char		*buf;
329698d5317Sjmmv 	size_t		 len, used;
330e271dbb8Schristos 	const int	 flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL;
3315494e770Schristos 	const size_t	 width = 200;
332698d5317Sjmmv 
333698d5317Sjmmv 	len = pb->size;
334698d5317Sjmmv 	if (len > width)
335698d5317Sjmmv 		len = width;
3365494e770Schristos 	buf = xreallocarray(NULL, len, 4 + 4);
337698d5317Sjmmv 
3385494e770Schristos 	used = utf8_strvis(buf, pb->data, len, flags);
339928fc495Schristos 	if (pb->size > width || used > width)
3405494e770Schristos 		strlcpy(buf + width, "...", 4);
341698d5317Sjmmv 	return (buf);
342698d5317Sjmmv }
343