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