1*232cb8cbSnicm /* $OpenBSD: paste.c,v 1.47 2024/10/12 08:13:52 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm 217d053cf9Snicm #include <stdlib.h> 22311827fbSnicm #include <string.h> 2345b608c5Snicm #include <time.h> 245f093833Snicm #include <vis.h> 25311827fbSnicm 26311827fbSnicm #include "tmux.h" 27311827fbSnicm 285bde14a0Snicm /* 29a41fa27aSnicm * Set of paste buffers. Note that paste buffer data is not necessarily a C 305bde14a0Snicm * string! 315bde14a0Snicm */ 325bde14a0Snicm 33909ecc87Snicm struct paste_buffer { 34909ecc87Snicm char *data; 35909ecc87Snicm size_t size; 36909ecc87Snicm 37909ecc87Snicm char *name; 38bba10b08Snicm time_t created; 39909ecc87Snicm int automatic; 40909ecc87Snicm u_int order; 41909ecc87Snicm 42909ecc87Snicm RB_ENTRY(paste_buffer) name_entry; 43909ecc87Snicm RB_ENTRY(paste_buffer) time_entry; 44909ecc87Snicm }; 45909ecc87Snicm 468d2662b6Snicm static u_int paste_next_index; 478d2662b6Snicm static u_int paste_next_order; 488d2662b6Snicm static u_int paste_num_automatic; 49c97fab4eSnicm static RB_HEAD(paste_name_tree, paste_buffer) paste_by_name; 50c97fab4eSnicm static RB_HEAD(paste_time_tree, paste_buffer) paste_by_time; 51ca678466Snicm 528d2662b6Snicm static int paste_cmp_names(const struct paste_buffer *, 538d2662b6Snicm const struct paste_buffer *); 548d2662b6Snicm RB_GENERATE_STATIC(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); 55a41fa27aSnicm 568d2662b6Snicm static int paste_cmp_times(const struct paste_buffer *, 578d2662b6Snicm const struct paste_buffer *); 588d2662b6Snicm RB_GENERATE_STATIC(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); 59a41fa27aSnicm 608d2662b6Snicm static int 61a41fa27aSnicm paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b) 62311827fbSnicm { 63a41fa27aSnicm return (strcmp(a->name, b->name)); 64311827fbSnicm } 65311827fbSnicm 668d2662b6Snicm static int 67a41fa27aSnicm paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) 68a41fa27aSnicm { 69a41fa27aSnicm if (a->order > b->order) 70a41fa27aSnicm return (-1); 71a41fa27aSnicm if (a->order < b->order) 72a41fa27aSnicm return (1); 73a41fa27aSnicm return (0); 74a41fa27aSnicm } 75a41fa27aSnicm 76909ecc87Snicm /* Get paste buffer name. */ 77909ecc87Snicm const char * 78909ecc87Snicm paste_buffer_name(struct paste_buffer *pb) 79909ecc87Snicm { 80909ecc87Snicm return (pb->name); 81909ecc87Snicm } 82909ecc87Snicm 83bba10b08Snicm /* Get paste buffer order. */ 84bba10b08Snicm u_int 85bba10b08Snicm paste_buffer_order(struct paste_buffer *pb) 86bba10b08Snicm { 87bba10b08Snicm return (pb->order); 88bba10b08Snicm } 89bba10b08Snicm 90bba10b08Snicm /* Get paste buffer created. */ 91bba10b08Snicm time_t 92bba10b08Snicm paste_buffer_created(struct paste_buffer *pb) 93bba10b08Snicm { 94bba10b08Snicm return (pb->created); 95bba10b08Snicm } 96bba10b08Snicm 97909ecc87Snicm /* Get paste buffer data. */ 98909ecc87Snicm const char * 99909ecc87Snicm paste_buffer_data(struct paste_buffer *pb, size_t *size) 100909ecc87Snicm { 101909ecc87Snicm if (size != NULL) 102909ecc87Snicm *size = pb->size; 103909ecc87Snicm return (pb->data); 104909ecc87Snicm } 105909ecc87Snicm 106bba10b08Snicm /* Walk paste buffers by time. */ 107a41fa27aSnicm struct paste_buffer * 108a41fa27aSnicm paste_walk(struct paste_buffer *pb) 109a41fa27aSnicm { 110a41fa27aSnicm if (pb == NULL) 111a41fa27aSnicm return (RB_MIN(paste_time_tree, &paste_by_time)); 112a41fa27aSnicm return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); 113a41fa27aSnicm } 114a41fa27aSnicm 115e5d942bbSnicm int 116e5d942bbSnicm paste_is_empty(void) 117e5d942bbSnicm { 118e5d942bbSnicm return RB_ROOT(&paste_by_time) == NULL; 119e5d942bbSnicm } 120e5d942bbSnicm 121a5427204Snicm /* Get the most recent automatic buffer. */ 122311827fbSnicm struct paste_buffer * 123909ecc87Snicm paste_get_top(const char **name) 124311827fbSnicm { 125a41fa27aSnicm struct paste_buffer *pb; 126a41fa27aSnicm 127a41fa27aSnicm pb = RB_MIN(paste_time_tree, &paste_by_time); 128e5d942bbSnicm while (pb != NULL && !pb->automatic) 129e5d942bbSnicm pb = RB_NEXT(paste_time_tree, &paste_by_time, pb); 130a41fa27aSnicm if (pb == NULL) 131311827fbSnicm return (NULL); 132909ecc87Snicm if (name != NULL) 133909ecc87Snicm *name = pb->name; 134a41fa27aSnicm return (pb); 135311827fbSnicm } 136311827fbSnicm 137a41fa27aSnicm /* Get a paste buffer by name. */ 138a41fa27aSnicm struct paste_buffer * 139a41fa27aSnicm paste_get_name(const char *name) 140311827fbSnicm { 141a41fa27aSnicm struct paste_buffer pbfind; 142311827fbSnicm 143a41fa27aSnicm if (name == NULL || *name == '\0') 144a41fa27aSnicm return (NULL); 145a41fa27aSnicm 146a41fa27aSnicm pbfind.name = (char *)name; 147a41fa27aSnicm return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); 148a41fa27aSnicm } 149a41fa27aSnicm 15064185147Snicm /* Free a paste buffer. */ 15164185147Snicm void 15264185147Snicm paste_free(struct paste_buffer *pb) 153a41fa27aSnicm { 15499acee14Snicm notify_paste_buffer(pb->name, 1); 155d79e52f9Snicm 156a41fa27aSnicm RB_REMOVE(paste_name_tree, &paste_by_name, pb); 157a41fa27aSnicm RB_REMOVE(paste_time_tree, &paste_by_time, pb); 158a41fa27aSnicm if (pb->automatic) 159a41fa27aSnicm paste_num_automatic--; 160311827fbSnicm 1617d053cf9Snicm free(pb->data); 162a41fa27aSnicm free(pb->name); 1637d053cf9Snicm free(pb); 164311827fbSnicm } 165311827fbSnicm 1665bde14a0Snicm /* 167a41fa27aSnicm * Add an automatic buffer, freeing the oldest automatic item if at limit. Note 1685bde14a0Snicm * that the caller is responsible for allocating data. 1695bde14a0Snicm */ 170311827fbSnicm void 1714aafca52Snicm paste_add(const char *prefix, char *data, size_t size) 172311827fbSnicm { 173a41fa27aSnicm struct paste_buffer *pb, *pb1; 174a41fa27aSnicm u_int limit; 175311827fbSnicm 1764aafca52Snicm if (prefix == NULL) 1774aafca52Snicm prefix = "buffer"; 1784aafca52Snicm 17901c63002Snicm if (size == 0) { 18001c63002Snicm free(data); 18192e1fe33Snicm return; 18201c63002Snicm } 18392e1fe33Snicm 184d89252e5Snicm limit = options_get_number(global_options, "buffer-limit"); 185a41fa27aSnicm RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { 186a41fa27aSnicm if (paste_num_automatic < limit) 187a41fa27aSnicm break; 188a41fa27aSnicm if (pb->automatic) 18964185147Snicm paste_free(pb); 1906e06e05fSnicm } 191311827fbSnicm 192311827fbSnicm pb = xmalloc(sizeof *pb); 193a41fa27aSnicm 194a41fa27aSnicm pb->name = NULL; 195a41fa27aSnicm do { 196a41fa27aSnicm free(pb->name); 1974aafca52Snicm xasprintf(&pb->name, "%s%u", prefix, paste_next_index); 198a41fa27aSnicm paste_next_index++; 199a41fa27aSnicm } while (paste_get_name(pb->name) != NULL); 200311827fbSnicm 201311827fbSnicm pb->data = data; 2021937104bSnicm pb->size = size; 203a41fa27aSnicm 204a41fa27aSnicm pb->automatic = 1; 205a41fa27aSnicm paste_num_automatic++; 206a41fa27aSnicm 207bba10b08Snicm pb->created = time(NULL); 208bba10b08Snicm 209a41fa27aSnicm pb->order = paste_next_order++; 210a41fa27aSnicm RB_INSERT(paste_name_tree, &paste_by_name, pb); 211a41fa27aSnicm RB_INSERT(paste_time_tree, &paste_by_time, pb); 212d79e52f9Snicm 21399acee14Snicm notify_paste_buffer(pb->name, 0); 214311827fbSnicm } 215311827fbSnicm 216a41fa27aSnicm /* Rename a paste buffer. */ 217a41fa27aSnicm int 218a41fa27aSnicm paste_rename(const char *oldname, const char *newname, char **cause) 219a41fa27aSnicm { 220b736ab22Snicm struct paste_buffer *pb, *pb_new; 221a41fa27aSnicm 222a41fa27aSnicm if (cause != NULL) 223a41fa27aSnicm *cause = NULL; 224a41fa27aSnicm 225a41fa27aSnicm if (oldname == NULL || *oldname == '\0') { 226a41fa27aSnicm if (cause != NULL) 227a41fa27aSnicm *cause = xstrdup("no buffer"); 228a41fa27aSnicm return (-1); 229a41fa27aSnicm } 230a41fa27aSnicm if (newname == NULL || *newname == '\0') { 231a41fa27aSnicm if (cause != NULL) 232a41fa27aSnicm *cause = xstrdup("new name is empty"); 233a41fa27aSnicm return (-1); 234a41fa27aSnicm } 235a41fa27aSnicm 236a41fa27aSnicm pb = paste_get_name(oldname); 237a41fa27aSnicm if (pb == NULL) { 238a41fa27aSnicm if (cause != NULL) 239a41fa27aSnicm xasprintf(cause, "no buffer %s", oldname); 240a41fa27aSnicm return (-1); 241a41fa27aSnicm } 242a41fa27aSnicm 243b736ab22Snicm pb_new = paste_get_name(newname); 244*232cb8cbSnicm if (pb_new == pb) 245*232cb8cbSnicm return (0); 24649828476Snicm if (pb_new != NULL) 24749828476Snicm paste_free(pb_new); 248b736ab22Snicm 249a41fa27aSnicm RB_REMOVE(paste_name_tree, &paste_by_name, pb); 250a41fa27aSnicm 251a41fa27aSnicm free(pb->name); 252a41fa27aSnicm pb->name = xstrdup(newname); 253a41fa27aSnicm 254a41fa27aSnicm if (pb->automatic) 255a41fa27aSnicm paste_num_automatic--; 256a41fa27aSnicm pb->automatic = 0; 257a41fa27aSnicm 258a41fa27aSnicm RB_INSERT(paste_name_tree, &paste_by_name, pb); 259a41fa27aSnicm 26099acee14Snicm notify_paste_buffer(oldname, 1); 26199acee14Snicm notify_paste_buffer(newname, 0); 262d79e52f9Snicm 263a41fa27aSnicm return (0); 264a41fa27aSnicm } 2655bde14a0Snicm 2665bde14a0Snicm /* 267a41fa27aSnicm * Add or replace an item in the store. Note that the caller is responsible for 2685bde14a0Snicm * allocating data. 2695bde14a0Snicm */ 270311827fbSnicm int 271a41fa27aSnicm paste_set(char *data, size_t size, const char *name, char **cause) 272311827fbSnicm { 27364185147Snicm struct paste_buffer *pb, *old; 274311827fbSnicm 275a41fa27aSnicm if (cause != NULL) 276a41fa27aSnicm *cause = NULL; 277a41fa27aSnicm 2781ac29a77Snicm if (size == 0) { 2791ac29a77Snicm free(data); 2805bde14a0Snicm return (0); 2811ac29a77Snicm } 282a41fa27aSnicm if (name == NULL) { 2834aafca52Snicm paste_add(NULL, data, size); 284a41fa27aSnicm return (0); 285a41fa27aSnicm } 2865bde14a0Snicm 287a41fa27aSnicm if (*name == '\0') { 288a41fa27aSnicm if (cause != NULL) 289a41fa27aSnicm *cause = xstrdup("empty buffer name"); 290311827fbSnicm return (-1); 291a41fa27aSnicm } 292311827fbSnicm 293a41fa27aSnicm pb = xmalloc(sizeof *pb); 294a41fa27aSnicm 295a41fa27aSnicm pb->name = xstrdup(name); 296311827fbSnicm 297311827fbSnicm pb->data = data; 2981937104bSnicm pb->size = size; 299311827fbSnicm 300a41fa27aSnicm pb->automatic = 0; 301a41fa27aSnicm pb->order = paste_next_order++; 302a41fa27aSnicm 303bba10b08Snicm pb->created = time(NULL); 304bba10b08Snicm 30564185147Snicm if ((old = paste_get_name(name)) != NULL) 30664185147Snicm paste_free(old); 3070d382a95Snicm 308a41fa27aSnicm RB_INSERT(paste_name_tree, &paste_by_name, pb); 309a41fa27aSnicm RB_INSERT(paste_time_tree, &paste_by_time, pb); 310a41fa27aSnicm 31199acee14Snicm notify_paste_buffer(name, 0); 312d79e52f9Snicm 313311827fbSnicm return (0); 314311827fbSnicm } 3155f093833Snicm 316a6c9106fSnicm /* Set paste data without otherwise changing it. */ 317a6c9106fSnicm void 318a6c9106fSnicm paste_replace(struct paste_buffer *pb, char *data, size_t size) 319a6c9106fSnicm { 320a6c9106fSnicm free(pb->data); 321a6c9106fSnicm pb->data = data; 322a6c9106fSnicm pb->size = size; 323d79e52f9Snicm 32499acee14Snicm notify_paste_buffer(pb->name, 0); 325a6c9106fSnicm } 326a6c9106fSnicm 327dbbd1b46Snicm /* Convert start of buffer into a nice string. */ 3285f093833Snicm char * 329f650d6adSnicm paste_make_sample(struct paste_buffer *pb) 3305f093833Snicm { 3315f093833Snicm char *buf; 3325f093833Snicm size_t len, used; 333da484ce1Snicm const int flags = VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL; 334dbbd1b46Snicm const size_t width = 200; 3355f093833Snicm 3365f093833Snicm len = pb->size; 3375f093833Snicm if (len > width) 3385f093833Snicm len = width; 33964cf113cSnicm buf = xreallocarray(NULL, len, 4 + 4); 3405f093833Snicm 341dbbd1b46Snicm used = utf8_strvis(buf, pb->data, len, flags); 342416cec24Snicm if (pb->size > width || used > width) 343dbbd1b46Snicm strlcpy(buf + width, "...", 4); 3445f093833Snicm return (buf); 3455f093833Snicm } 346