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