1 /* $OpenBSD: paste.c,v 1.28 2015/08/29 09:25:00 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 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 /* Free the most recent buffer. */ 115 int 116 paste_free_top(void) 117 { 118 struct paste_buffer *pb; 119 120 pb = paste_get_top(NULL); 121 if (pb == NULL) 122 return (-1); 123 return (paste_free_name(pb->name)); 124 } 125 126 /* Get a paste buffer by name. */ 127 struct paste_buffer * 128 paste_get_name(const char *name) 129 { 130 struct paste_buffer pbfind; 131 132 if (name == NULL || *name == '\0') 133 return (NULL); 134 135 pbfind.name = (char *)name; 136 return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); 137 } 138 139 /* Free a paste buffer by name. */ 140 int 141 paste_free_name(const char *name) 142 { 143 struct paste_buffer *pb, pbfind; 144 145 if (name == NULL || *name == '\0') 146 return (-1); 147 148 pbfind.name = (char *)name; 149 pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind); 150 if (pb == NULL) 151 return (-1); 152 153 RB_REMOVE(paste_name_tree, &paste_by_name, pb); 154 RB_REMOVE(paste_time_tree, &paste_by_time, pb); 155 if (pb->automatic) 156 paste_num_automatic--; 157 158 free(pb->data); 159 free(pb->name); 160 free(pb); 161 return (0); 162 } 163 164 /* 165 * Add an automatic buffer, freeing the oldest automatic item if at limit. Note 166 * that the caller is responsible for allocating data. 167 */ 168 void 169 paste_add(char *data, size_t size) 170 { 171 struct paste_buffer *pb, *pb1; 172 u_int limit; 173 174 if (size == 0) 175 return; 176 177 limit = options_get_number(&global_options, "buffer-limit"); 178 RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { 179 if (paste_num_automatic < limit) 180 break; 181 if (pb->automatic) 182 paste_free_name(pb->name); 183 } 184 185 pb = xmalloc(sizeof *pb); 186 187 pb->name = NULL; 188 do { 189 free(pb->name); 190 xasprintf(&pb->name, "buffer%04u", paste_next_index); 191 paste_next_index++; 192 } while (paste_get_name(pb->name) != NULL); 193 194 pb->data = data; 195 pb->size = size; 196 197 pb->automatic = 1; 198 paste_num_automatic++; 199 200 pb->order = paste_next_order++; 201 RB_INSERT(paste_name_tree, &paste_by_name, pb); 202 RB_INSERT(paste_time_tree, &paste_by_time, pb); 203 } 204 205 /* Rename a paste buffer. */ 206 int 207 paste_rename(const char *oldname, const char *newname, char **cause) 208 { 209 struct paste_buffer *pb, *pb_new; 210 211 if (cause != NULL) 212 *cause = NULL; 213 214 if (oldname == NULL || *oldname == '\0') { 215 if (cause != NULL) 216 *cause = xstrdup("no buffer"); 217 return (-1); 218 } 219 if (newname == NULL || *newname == '\0') { 220 if (cause != NULL) 221 *cause = xstrdup("new name is empty"); 222 return (-1); 223 } 224 225 pb = paste_get_name(oldname); 226 if (pb == NULL) { 227 if (cause != NULL) 228 xasprintf(cause, "no buffer %s", oldname); 229 return (-1); 230 } 231 232 pb_new = paste_get_name(newname); 233 if (pb_new != NULL) { 234 if (cause != NULL) 235 xasprintf(cause, "buffer %s already exists", newname); 236 return (-1); 237 } 238 239 RB_REMOVE(paste_name_tree, &paste_by_name, pb); 240 241 free(pb->name); 242 pb->name = xstrdup(newname); 243 244 if (pb->automatic) 245 paste_num_automatic--; 246 pb->automatic = 0; 247 248 RB_INSERT(paste_name_tree, &paste_by_name, pb); 249 250 return (0); 251 } 252 253 /* 254 * Add or replace an item in the store. Note that the caller is responsible for 255 * allocating data. 256 */ 257 int 258 paste_set(char *data, size_t size, const char *name, char **cause) 259 { 260 struct paste_buffer *pb; 261 262 if (cause != NULL) 263 *cause = NULL; 264 265 if (size == 0) { 266 free(data); 267 return (0); 268 } 269 if (name == NULL) { 270 paste_add(data, size); 271 return (0); 272 } 273 274 if (*name == '\0') { 275 if (cause != NULL) 276 *cause = xstrdup("empty buffer name"); 277 return (-1); 278 } 279 280 281 pb = xmalloc(sizeof *pb); 282 283 pb->name = xstrdup(name); 284 285 pb->data = data; 286 pb->size = size; 287 288 pb->automatic = 0; 289 pb->order = paste_next_order++; 290 291 if (paste_get_name(name) != NULL) 292 paste_free_name(name); 293 294 RB_INSERT(paste_name_tree, &paste_by_name, pb); 295 RB_INSERT(paste_time_tree, &paste_by_time, pb); 296 297 return (0); 298 } 299 300 /* Convert start of buffer into a nice string. */ 301 char * 302 paste_make_sample(struct paste_buffer *pb, int utf8flag) 303 { 304 char *buf; 305 size_t len, used; 306 const int flags = VIS_OCTAL|VIS_TAB|VIS_NL; 307 const size_t width = 200; 308 309 len = pb->size; 310 if (len > width) 311 len = width; 312 buf = xreallocarray(NULL, len, 4 + 4); 313 314 if (utf8flag) 315 used = utf8_strvis(buf, pb->data, len, flags); 316 else 317 used = strvisx(buf, pb->data, len, flags); 318 if (pb->size > width || used > width) 319 strlcpy(buf + width, "...", 4); 320 return (buf); 321 } 322 323 /* Paste into a window pane, filtering '\n' according to separator. */ 324 void 325 paste_send_pane(struct paste_buffer *pb, struct window_pane *wp, 326 const char *sep, int bracket) 327 { 328 const char *data = pb->data, *end = data + pb->size, *lf; 329 size_t seplen; 330 331 if (wp->flags & PANE_INPUTOFF) 332 return; 333 334 if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) 335 bufferevent_write(wp->event, "\033[200~", 6); 336 337 seplen = strlen(sep); 338 while ((lf = memchr(data, '\n', end - data)) != NULL) { 339 if (lf != data) 340 bufferevent_write(wp->event, data, lf - data); 341 bufferevent_write(wp->event, sep, seplen); 342 data = lf + 1; 343 } 344 345 if (end != data) 346 bufferevent_write(wp->event, data, end - data); 347 348 if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) 349 bufferevent_write(wp->event, "\033[201~", 6); 350 } 351