1 /* $OpenBSD: paste.c,v 1.21 2014/06/20 11:00:19 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 u_int paste_next_index; 34 u_int paste_next_order; 35 u_int paste_num_automatic; 36 RB_HEAD(paste_name_tree, paste_buffer) paste_by_name; 37 RB_HEAD(paste_time_tree, paste_buffer) paste_by_time; 38 39 int paste_cmp_names(const struct paste_buffer *, const struct paste_buffer *); 40 RB_PROTOTYPE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); 41 RB_GENERATE(paste_name_tree, paste_buffer, name_entry, paste_cmp_names); 42 43 int paste_cmp_times(const struct paste_buffer *, const struct paste_buffer *); 44 RB_PROTOTYPE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); 45 RB_GENERATE(paste_time_tree, paste_buffer, time_entry, paste_cmp_times); 46 47 int 48 paste_cmp_names(const struct paste_buffer *a, const struct paste_buffer *b) 49 { 50 return (strcmp(a->name, b->name)); 51 } 52 53 int 54 paste_cmp_times(const struct paste_buffer *a, const struct paste_buffer *b) 55 { 56 if (a->order > b->order) 57 return (-1); 58 if (a->order < b->order) 59 return (1); 60 return (0); 61 } 62 63 /* Walk paste buffers by name. */ 64 struct paste_buffer * 65 paste_walk(struct paste_buffer *pb) 66 { 67 if (pb == NULL) 68 return (RB_MIN(paste_time_tree, &paste_by_time)); 69 return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); 70 } 71 72 /* Get the most recent automatic buffer. */ 73 struct paste_buffer * 74 paste_get_top(void) 75 { 76 struct paste_buffer *pb; 77 78 pb = RB_MIN(paste_time_tree, &paste_by_time); 79 if (pb == NULL) 80 return (NULL); 81 return (pb); 82 } 83 84 /* Free the most recent buffer. */ 85 int 86 paste_free_top(void) 87 { 88 struct paste_buffer *pb; 89 90 pb = paste_get_top(); 91 if (pb == NULL) 92 return (-1); 93 return (paste_free_name(pb->name)); 94 } 95 96 /* Get a paste buffer by name. */ 97 struct paste_buffer * 98 paste_get_name(const char *name) 99 { 100 struct paste_buffer pbfind; 101 102 if (name == NULL || *name == '\0') 103 return (NULL); 104 105 pbfind.name = (char*)name; 106 return (RB_FIND(paste_name_tree, &paste_by_name, &pbfind)); 107 } 108 109 /* Free a paste buffer by name. */ 110 int 111 paste_free_name(const char *name) 112 { 113 struct paste_buffer *pb, pbfind; 114 115 if (name == NULL || *name == '\0') 116 return (-1); 117 118 pbfind.name = (char*)name; 119 pb = RB_FIND(paste_name_tree, &paste_by_name, &pbfind); 120 if (pb == NULL) 121 return (-1); 122 123 RB_REMOVE(paste_name_tree, &paste_by_name, pb); 124 RB_REMOVE(paste_time_tree, &paste_by_time, pb); 125 if (pb->automatic) 126 paste_num_automatic--; 127 128 free(pb->data); 129 free(pb->name); 130 free(pb); 131 return (0); 132 } 133 134 /* 135 * Add an automatic buffer, freeing the oldest automatic item if at limit. Note 136 * that the caller is responsible for allocating data. 137 */ 138 void 139 paste_add(char *data, size_t size) 140 { 141 struct paste_buffer *pb, *pb1; 142 u_int limit; 143 144 if (size == 0) 145 return; 146 147 limit = options_get_number(&global_options, "buffer-limit"); 148 RB_FOREACH_REVERSE_SAFE(pb, paste_time_tree, &paste_by_time, pb1) { 149 if (paste_num_automatic < limit) 150 break; 151 if (pb->automatic) 152 paste_free_name(pb->name); 153 } 154 155 pb = xmalloc(sizeof *pb); 156 157 pb->name = NULL; 158 do { 159 free(pb->name); 160 xasprintf(&pb->name, "buffer%04u", paste_next_index); 161 paste_next_index++; 162 } while (paste_get_name(pb->name) != NULL); 163 164 pb->data = data; 165 pb->size = size; 166 167 pb->automatic = 1; 168 paste_num_automatic++; 169 170 pb->order = paste_next_order++; 171 RB_INSERT(paste_name_tree, &paste_by_name, pb); 172 RB_INSERT(paste_time_tree, &paste_by_time, pb); 173 } 174 175 /* Rename a paste buffer. */ 176 int 177 paste_rename(const char *oldname, const char *newname, char **cause) 178 { 179 struct paste_buffer *pb, *pb_new; 180 181 if (cause != NULL) 182 *cause = NULL; 183 184 if (oldname == NULL || *oldname == '\0') { 185 if (cause != NULL) 186 *cause = xstrdup("no buffer"); 187 return (-1); 188 } 189 if (newname == NULL || *newname == '\0') { 190 if (cause != NULL) 191 *cause = xstrdup("new name is empty"); 192 return (-1); 193 } 194 195 pb = paste_get_name(oldname); 196 if (pb == NULL) { 197 if (cause != NULL) 198 xasprintf(cause, "no buffer %s", oldname); 199 return (-1); 200 } 201 202 pb_new = paste_get_name(newname); 203 if (pb_new != NULL) { 204 if (cause != NULL) 205 xasprintf(cause, "buffer %s already exists", newname); 206 return (-1); 207 } 208 209 RB_REMOVE(paste_name_tree, &paste_by_name, pb); 210 211 free(pb->name); 212 pb->name = xstrdup(newname); 213 214 if (pb->automatic) 215 paste_num_automatic--; 216 pb->automatic = 0; 217 218 RB_INSERT(paste_name_tree, &paste_by_name, pb); 219 220 return (0); 221 } 222 223 /* 224 * Add or replace an item in the store. Note that the caller is responsible for 225 * allocating data. 226 */ 227 int 228 paste_set(char *data, size_t size, const char *name, char **cause) 229 { 230 struct paste_buffer *pb; 231 232 if (cause != NULL) 233 *cause = NULL; 234 235 if (size == 0) { 236 free(data); 237 return (0); 238 } 239 if (name == NULL) { 240 paste_add(data, size); 241 return (0); 242 } 243 244 if (*name == '\0') { 245 if (cause != NULL) 246 *cause = xstrdup("empty buffer name"); 247 return (-1); 248 } 249 250 pb = paste_get_name(name); 251 if (pb != NULL) 252 paste_free_name(name); 253 254 pb = xmalloc(sizeof *pb); 255 256 pb->name = xstrdup(name); 257 258 pb->data = data; 259 pb->size = size; 260 261 pb->automatic = 0; 262 pb->order = paste_next_order++; 263 264 RB_INSERT(paste_name_tree, &paste_by_name, pb); 265 RB_INSERT(paste_time_tree, &paste_by_time, pb); 266 267 return (0); 268 } 269 270 /* Convert start of buffer into a nice string. */ 271 char * 272 paste_make_sample(struct paste_buffer *pb, int utf8flag) 273 { 274 char *buf; 275 size_t len, used; 276 const int flags = VIS_OCTAL|VIS_TAB|VIS_NL; 277 const size_t width = 200; 278 279 len = pb->size; 280 if (len > width) 281 len = width; 282 buf = xmalloc(len * 4 + 4); 283 284 if (utf8flag) 285 used = utf8_strvis(buf, pb->data, len, flags); 286 else 287 used = strvisx(buf, pb->data, len, flags); 288 if (pb->size > width || used > width) 289 strlcpy(buf + width, "...", 4); 290 return (buf); 291 } 292 293 /* Paste into a window pane, filtering '\n' according to separator. */ 294 void 295 paste_send_pane(struct paste_buffer *pb, struct window_pane *wp, 296 const char *sep, int bracket) 297 { 298 const char *data = pb->data, *end = data + pb->size, *lf; 299 size_t seplen; 300 301 if (bracket) 302 bufferevent_write(wp->event, "\033[200~", 6); 303 304 seplen = strlen(sep); 305 while ((lf = memchr(data, '\n', end - data)) != NULL) { 306 if (lf != data) 307 bufferevent_write(wp->event, data, lf - data); 308 bufferevent_write(wp->event, sep, seplen); 309 data = lf + 1; 310 } 311 312 if (end != data) 313 bufferevent_write(wp->event, data, end - data); 314 315 if (bracket) 316 bufferevent_write(wp->event, "\033[201~", 6); 317 } 318