1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2017 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 21 #include <stdlib.h> 22 #include <string.h> 23 #include <time.h> 24 25 #include "tmux.h" 26 27 static struct screen *window_buffer_init(struct window_mode_entry *, 28 struct cmd_find_state *, struct args *); 29 static void window_buffer_free(struct window_mode_entry *); 30 static void window_buffer_resize(struct window_mode_entry *, u_int, 31 u_int); 32 static void window_buffer_key(struct window_mode_entry *, 33 struct client *, struct session *, 34 struct winlink *, key_code, struct mouse_event *); 35 36 #define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -b '%%'" 37 38 #define WINDOW_BUFFER_DEFAULT_FORMAT \ 39 "#{buffer_size} bytes (#{t:buffer_created})" 40 41 static const struct menu_item window_buffer_menu_items[] = { 42 { "Paste", 'p', NULL }, 43 { "Paste Tagged", 'P', NULL }, 44 { "", KEYC_NONE, NULL }, 45 { "Tag", 't', NULL }, 46 { "Tag All", '\024', NULL }, 47 { "Tag None", 'T', NULL }, 48 { "", KEYC_NONE, NULL }, 49 { "Delete", 'd', NULL }, 50 { "Delete Tagged", 'D', NULL }, 51 { "", KEYC_NONE, NULL }, 52 { "Cancel", 'q', NULL }, 53 54 { NULL, KEYC_NONE, NULL } 55 }; 56 57 const struct window_mode window_buffer_mode = { 58 .name = "buffer-mode", 59 .default_format = WINDOW_BUFFER_DEFAULT_FORMAT, 60 61 .init = window_buffer_init, 62 .free = window_buffer_free, 63 .resize = window_buffer_resize, 64 .key = window_buffer_key, 65 }; 66 67 enum window_buffer_sort_type { 68 WINDOW_BUFFER_BY_TIME, 69 WINDOW_BUFFER_BY_NAME, 70 WINDOW_BUFFER_BY_SIZE, 71 }; 72 static const char *window_buffer_sort_list[] = { 73 "time", 74 "name", 75 "size" 76 }; 77 78 struct window_buffer_itemdata { 79 const char *name; 80 u_int order; 81 size_t size; 82 }; 83 84 struct window_buffer_modedata { 85 struct window_pane *wp; 86 struct cmd_find_state fs; 87 88 struct mode_tree_data *data; 89 char *command; 90 char *format; 91 92 struct window_buffer_itemdata **item_list; 93 u_int item_size; 94 }; 95 96 static struct window_buffer_itemdata * 97 window_buffer_add_item(struct window_buffer_modedata *data) 98 { 99 struct window_buffer_itemdata *item; 100 101 data->item_list = xreallocarray(data->item_list, data->item_size + 1, 102 sizeof *data->item_list); 103 item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item); 104 return (item); 105 } 106 107 static void 108 window_buffer_free_item(struct window_buffer_itemdata *item) 109 { 110 free(__UNCONST(item->name)); 111 free(item); 112 } 113 114 static int 115 window_buffer_cmp_name(const void *a0, const void *b0) 116 { 117 const struct window_buffer_itemdata *const *a = a0; 118 const struct window_buffer_itemdata *const *b = b0; 119 120 return (strcmp((*a)->name, (*b)->name)); 121 } 122 123 static int 124 window_buffer_cmp_time(const void *a0, const void *b0) 125 { 126 const struct window_buffer_itemdata *const *a = a0; 127 const struct window_buffer_itemdata *const *b = b0; 128 129 if ((*a)->order > (*b)->order) 130 return (-1); 131 if ((*a)->order < (*b)->order) 132 return (1); 133 return (strcmp((*a)->name, (*b)->name)); 134 } 135 136 static int 137 window_buffer_cmp_size(const void *a0, const void *b0) 138 { 139 const struct window_buffer_itemdata *const *a = a0; 140 const struct window_buffer_itemdata *const *b = b0; 141 142 if ((*a)->size > (*b)->size) 143 return (-1); 144 if ((*a)->size < (*b)->size) 145 return (1); 146 return (strcmp((*a)->name, (*b)->name)); 147 } 148 149 static void 150 window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag, 151 const char *filter) 152 { 153 struct window_buffer_modedata *data = modedata; 154 struct window_buffer_itemdata *item; 155 u_int i; 156 struct paste_buffer *pb; 157 char *text, *cp; 158 struct format_tree *ft; 159 struct session *s = NULL; 160 struct winlink *wl = NULL; 161 struct window_pane *wp = NULL; 162 163 for (i = 0; i < data->item_size; i++) 164 window_buffer_free_item(data->item_list[i]); 165 free(data->item_list); 166 data->item_list = NULL; 167 data->item_size = 0; 168 169 pb = NULL; 170 while ((pb = paste_walk(pb)) != NULL) { 171 item = window_buffer_add_item(data); 172 item->name = xstrdup(paste_buffer_name(pb)); 173 paste_buffer_data(pb, &item->size); 174 item->order = paste_buffer_order(pb); 175 } 176 177 switch (sort_type) { 178 case WINDOW_BUFFER_BY_NAME: 179 qsort(data->item_list, data->item_size, sizeof *data->item_list, 180 window_buffer_cmp_name); 181 break; 182 case WINDOW_BUFFER_BY_TIME: 183 qsort(data->item_list, data->item_size, sizeof *data->item_list, 184 window_buffer_cmp_time); 185 break; 186 case WINDOW_BUFFER_BY_SIZE: 187 qsort(data->item_list, data->item_size, sizeof *data->item_list, 188 window_buffer_cmp_size); 189 break; 190 } 191 192 if (cmd_find_valid_state(&data->fs)) { 193 s = data->fs.s; 194 wl = data->fs.wl; 195 wp = data->fs.wp; 196 } 197 198 for (i = 0; i < data->item_size; i++) { 199 item = data->item_list[i]; 200 201 pb = paste_get_name(item->name); 202 if (pb == NULL) 203 continue; 204 ft = format_create(NULL, NULL, FORMAT_NONE, 0); 205 format_defaults(ft, NULL, s, wl, wp); 206 format_defaults_paste_buffer(ft, pb); 207 208 if (filter != NULL) { 209 cp = format_expand(ft, filter); 210 if (!format_true(cp)) { 211 free(cp); 212 format_free(ft); 213 continue; 214 } 215 free(cp); 216 } 217 218 text = format_expand(ft, data->format); 219 mode_tree_add(data->data, NULL, item, item->order, item->name, 220 text, -1); 221 free(text); 222 223 format_free(ft); 224 } 225 226 } 227 228 static void 229 window_buffer_draw(__unused void *modedata, void *itemdata, 230 struct screen_write_ctx *ctx, u_int sx, u_int sy) 231 { 232 struct window_buffer_itemdata *item = itemdata; 233 struct paste_buffer *pb; 234 char line[1024]; 235 const char *pdata, *end, *cp; 236 size_t psize, at; 237 u_int i, cx = ctx->s->cx, cy = ctx->s->cy; 238 239 pb = paste_get_name(item->name); 240 if (pb == NULL) 241 return; 242 243 pdata = end = paste_buffer_data(pb, &psize); 244 for (i = 0; i < sy; i++) { 245 at = 0; 246 while (end != pdata + psize && *end != '\n') { 247 if ((sizeof line) - at > 5) { 248 cp = vis(line + at, *end, VIS_OCTAL|VIS_TAB, 0); 249 at = cp - line; 250 } 251 end++; 252 } 253 if (at > sx) 254 at = sx; 255 line[at] = '\0'; 256 257 if (*line != '\0') { 258 screen_write_cursormove(ctx, cx, cy + i, 0); 259 screen_write_puts(ctx, &grid_default_cell, "%s", line); 260 } 261 262 if (end == pdata + psize) 263 break; 264 end++; 265 } 266 } 267 268 static int 269 window_buffer_search(__unused void *modedata, void *itemdata, const char *ss) 270 { 271 struct window_buffer_itemdata *item = itemdata; 272 struct paste_buffer *pb; 273 const char *bufdata; 274 size_t bufsize; 275 276 if ((pb = paste_get_name(item->name)) == NULL) 277 return (0); 278 if (strstr(item->name, ss) != NULL) 279 return (1); 280 bufdata = paste_buffer_data(pb, &bufsize); 281 return (memmem(bufdata, bufsize, ss, strlen(ss)) != NULL); 282 } 283 284 static void 285 window_buffer_menu(void *modedata, struct client *c, key_code key) 286 { 287 struct window_buffer_modedata *data = modedata; 288 struct window_pane *wp = data->wp; 289 struct window_mode_entry *wme; 290 291 wme = TAILQ_FIRST(&wp->modes); 292 if (wme == NULL || wme->data != modedata) 293 return; 294 window_buffer_key(wme, c, NULL, NULL, key, NULL); 295 } 296 297 static struct screen * 298 window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, 299 struct args *args) 300 { 301 struct window_pane *wp = wme->wp; 302 struct window_buffer_modedata *data; 303 struct screen *s; 304 305 wme->data = data = xcalloc(1, sizeof *data); 306 data->wp = wp; 307 cmd_find_copy_state(&data->fs, fs); 308 309 if (args == NULL || !args_has(args, 'F')) 310 data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT); 311 else 312 data->format = xstrdup(args_get(args, 'F')); 313 if (args == NULL || args->argc == 0) 314 data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND); 315 else 316 data->command = xstrdup(args->argv[0]); 317 318 data->data = mode_tree_start(wp, args, window_buffer_build, 319 window_buffer_draw, window_buffer_search, window_buffer_menu, data, 320 window_buffer_menu_items, window_buffer_sort_list, 321 nitems(window_buffer_sort_list), &s); 322 mode_tree_zoom(data->data, args); 323 324 mode_tree_build(data->data); 325 mode_tree_draw(data->data); 326 327 return (s); 328 } 329 330 static void 331 window_buffer_free(struct window_mode_entry *wme) 332 { 333 struct window_buffer_modedata *data = wme->data; 334 u_int i; 335 336 if (data == NULL) 337 return; 338 339 mode_tree_free(data->data); 340 341 for (i = 0; i < data->item_size; i++) 342 window_buffer_free_item(data->item_list[i]); 343 free(data->item_list); 344 345 free(data->format); 346 free(data->command); 347 348 free(data); 349 } 350 351 static void 352 window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy) 353 { 354 struct window_buffer_modedata *data = wme->data; 355 356 mode_tree_resize(data->data, sx, sy); 357 } 358 359 static void 360 window_buffer_do_delete(void* modedata, void *itemdata, 361 __unused struct client *c, __unused key_code key) 362 { 363 struct window_buffer_modedata *data = modedata; 364 struct window_buffer_itemdata *item = itemdata; 365 struct paste_buffer *pb; 366 367 if (item == mode_tree_get_current(data->data)) 368 mode_tree_down(data->data, 0); 369 if ((pb = paste_get_name(item->name)) != NULL) 370 paste_free(pb); 371 } 372 373 static void 374 window_buffer_do_paste(void* modedata, void *itemdata, struct client *c, 375 __unused key_code key) 376 { 377 struct window_buffer_modedata *data = modedata; 378 struct window_buffer_itemdata *item = itemdata; 379 struct paste_buffer *pb; 380 381 if ((pb = paste_get_name(item->name)) != NULL) 382 mode_tree_run_command(c, NULL, data->command, item->name); 383 } 384 385 static void 386 window_buffer_key(struct window_mode_entry *wme, struct client *c, 387 __unused struct session *s, __unused struct winlink *wl, key_code key, 388 struct mouse_event *m) 389 { 390 struct window_pane *wp = wme->wp; 391 struct window_buffer_modedata *data = wme->data; 392 struct mode_tree_data *mtd = data->data; 393 struct window_buffer_itemdata *item; 394 int finished; 395 396 finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); 397 switch (key) { 398 case 'd': 399 item = mode_tree_get_current(mtd); 400 window_buffer_do_delete(data, item, c, key); 401 mode_tree_build(mtd); 402 break; 403 case 'D': 404 mode_tree_each_tagged(mtd, window_buffer_do_delete, c, key, 0); 405 mode_tree_build(mtd); 406 break; 407 case 'P': 408 mode_tree_each_tagged(mtd, window_buffer_do_paste, c, key, 0); 409 finished = 1; 410 break; 411 case 'p': 412 case '\r': 413 item = mode_tree_get_current(mtd); 414 window_buffer_do_paste(data, item, c, key); 415 finished = 1; 416 break; 417 } 418 if (finished || paste_get_top(NULL) == NULL) 419 window_pane_reset_mode(wp); 420 else { 421 mode_tree_draw(mtd); 422 wp->flags |= PANE_REDRAW; 423 } 424 } 425