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