xref: /openbsd-src/usr.bin/tmux/window-buffer.c (revision d32785554e5217899691b82c33b50ad710f1ce7e)
1*d3278555Snicm /* $OpenBSD: window-buffer.c,v 1.40 2024/08/04 08:53:43 nicm Exp $ */
2a42faf7dSnicm 
3a42faf7dSnicm /*
4a42faf7dSnicm  * Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
5a42faf7dSnicm  *
6a42faf7dSnicm  * Permission to use, copy, modify, and distribute this software for any
7a42faf7dSnicm  * purpose with or without fee is hereby granted, provided that the above
8a42faf7dSnicm  * copyright notice and this permission notice appear in all copies.
9a42faf7dSnicm  *
10a42faf7dSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a42faf7dSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a42faf7dSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a42faf7dSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a42faf7dSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15a42faf7dSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16a42faf7dSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a42faf7dSnicm  */
18a42faf7dSnicm 
19a42faf7dSnicm #include <sys/types.h>
20a42faf7dSnicm 
21a42faf7dSnicm #include <stdlib.h>
22a42faf7dSnicm #include <string.h>
2305835efcSnicm #include <time.h>
24a6c9106fSnicm #include <unistd.h>
25a42faf7dSnicm #include <vis.h>
26a42faf7dSnicm 
27a42faf7dSnicm #include "tmux.h"
28a42faf7dSnicm 
2930a94f45Snicm static struct screen	*window_buffer_init(struct window_mode_entry *,
30a42faf7dSnicm 			     struct cmd_find_state *, struct args *);
3130a94f45Snicm static void		 window_buffer_free(struct window_mode_entry *);
3230a94f45Snicm static void		 window_buffer_resize(struct window_mode_entry *, u_int,
33a42faf7dSnicm 			     u_int);
34734f37e4Snicm static void		 window_buffer_update(struct window_mode_entry *);
3530a94f45Snicm static void		 window_buffer_key(struct window_mode_entry *,
36bf52409eSnicm 			     struct client *, struct session *,
37bf52409eSnicm 			     struct winlink *, key_code, struct mouse_event *);
38a42faf7dSnicm 
392e03ad1fSnicm #define WINDOW_BUFFER_DEFAULT_COMMAND "paste-buffer -p -b '%%'"
40a42faf7dSnicm 
41bf38e336Snicm #define WINDOW_BUFFER_DEFAULT_FORMAT \
426c6f347cSnicm 	"#{t/p:buffer_created}: #{buffer_sample}"
43bf38e336Snicm 
44438eed14Snicm #define WINDOW_BUFFER_DEFAULT_KEY_FORMAT \
45438eed14Snicm 	"#{?#{e|<:#{line},10}," \
46438eed14Snicm 		"#{line}" \
47438eed14Snicm 	"," \
48438eed14Snicm 		"#{?#{e|<:#{line},36},"	\
49438eed14Snicm 	        	"M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \
50438eed14Snicm 		"," \
51438eed14Snicm 	        	"" \
52438eed14Snicm 		"}" \
53438eed14Snicm 	"}"
54438eed14Snicm 
551335341aSnicm static const struct menu_item window_buffer_menu_items[] = {
561335341aSnicm 	{ "Paste", 'p', NULL },
571335341aSnicm 	{ "Paste Tagged", 'P', NULL },
581335341aSnicm 	{ "", KEYC_NONE, NULL },
591335341aSnicm 	{ "Tag", 't', NULL },
601335341aSnicm 	{ "Tag All", '\024', NULL },
611335341aSnicm 	{ "Tag None", 'T', NULL },
621335341aSnicm 	{ "", KEYC_NONE, NULL },
631335341aSnicm 	{ "Delete", 'd', NULL },
641335341aSnicm 	{ "Delete Tagged", 'D', NULL },
651335341aSnicm 	{ "", KEYC_NONE, NULL },
661335341aSnicm 	{ "Cancel", 'q', NULL },
671335341aSnicm 
681335341aSnicm 	{ NULL, KEYC_NONE, NULL }
691335341aSnicm };
70f43bc87cSnicm 
71a42faf7dSnicm const struct window_mode window_buffer_mode = {
72a42faf7dSnicm 	.name = "buffer-mode",
7371431f24Snicm 	.default_format = WINDOW_BUFFER_DEFAULT_FORMAT,
74a42faf7dSnicm 
75a42faf7dSnicm 	.init = window_buffer_init,
76a42faf7dSnicm 	.free = window_buffer_free,
77a42faf7dSnicm 	.resize = window_buffer_resize,
78734f37e4Snicm 	.update = window_buffer_update,
79a42faf7dSnicm 	.key = window_buffer_key,
80a42faf7dSnicm };
81a42faf7dSnicm 
82a42faf7dSnicm enum window_buffer_sort_type {
83a42faf7dSnicm 	WINDOW_BUFFER_BY_TIME,
8438801d3eSnicm 	WINDOW_BUFFER_BY_NAME,
85a42faf7dSnicm 	WINDOW_BUFFER_BY_SIZE,
86a42faf7dSnicm };
87a42faf7dSnicm static const char *window_buffer_sort_list[] = {
88a42faf7dSnicm 	"time",
8938801d3eSnicm 	"name",
90a42faf7dSnicm 	"size"
91a42faf7dSnicm };
923f6f9f7dSnicm static struct mode_tree_sort_criteria *window_buffer_sort;
93a42faf7dSnicm 
94a42faf7dSnicm struct window_buffer_itemdata {
95a42faf7dSnicm 	const char	*name;
96a42faf7dSnicm 	u_int		 order;
97a42faf7dSnicm 	size_t		 size;
98a42faf7dSnicm };
99a42faf7dSnicm 
100a42faf7dSnicm struct window_buffer_modedata {
101f43bc87cSnicm 	struct window_pane		 *wp;
10200db7279Snicm 	struct cmd_find_state		  fs;
103f43bc87cSnicm 
104a42faf7dSnicm 	struct mode_tree_data		 *data;
105a42faf7dSnicm 	char				 *command;
106bf38e336Snicm 	char				 *format;
107438eed14Snicm 	char				 *key_format;
108a42faf7dSnicm 
109a42faf7dSnicm 	struct window_buffer_itemdata	**item_list;
110a42faf7dSnicm 	u_int				  item_size;
111a42faf7dSnicm };
112a42faf7dSnicm 
113a6c9106fSnicm struct window_buffer_editdata {
114a6c9106fSnicm 	u_int			 wp_id;
115a6c9106fSnicm 	char			*name;
116a6c9106fSnicm 	struct paste_buffer	*pb;
117a6c9106fSnicm };
118a6c9106fSnicm 
119a42faf7dSnicm static struct window_buffer_itemdata *
120a42faf7dSnicm window_buffer_add_item(struct window_buffer_modedata *data)
121a42faf7dSnicm {
122a42faf7dSnicm 	struct window_buffer_itemdata	*item;
123a42faf7dSnicm 
124a42faf7dSnicm 	data->item_list = xreallocarray(data->item_list, data->item_size + 1,
125a42faf7dSnicm 	    sizeof *data->item_list);
126a42faf7dSnicm 	item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
127a42faf7dSnicm 	return (item);
128a42faf7dSnicm }
129a42faf7dSnicm 
130a42faf7dSnicm static void
131a42faf7dSnicm window_buffer_free_item(struct window_buffer_itemdata *item)
132a42faf7dSnicm {
133a42faf7dSnicm 	free((void *)item->name);
134a42faf7dSnicm 	free(item);
135a42faf7dSnicm }
136a42faf7dSnicm 
137a42faf7dSnicm static int
1383f6f9f7dSnicm window_buffer_cmp(const void *a0, const void *b0)
139a42faf7dSnicm {
140a42faf7dSnicm 	const struct window_buffer_itemdata *const	*a = a0;
141a42faf7dSnicm 	const struct window_buffer_itemdata *const	*b = b0;
1423f6f9f7dSnicm 	int						 result = 0;
143a42faf7dSnicm 
1443f6f9f7dSnicm 	if (window_buffer_sort->field == WINDOW_BUFFER_BY_TIME)
1453f6f9f7dSnicm 		result = (*b)->order - (*a)->order;
1463f6f9f7dSnicm 	else if (window_buffer_sort->field == WINDOW_BUFFER_BY_SIZE)
1473f6f9f7dSnicm 		result = (*b)->size - (*a)->size;
148a42faf7dSnicm 
1493f6f9f7dSnicm 	/* Use WINDOW_BUFFER_BY_NAME as default order and tie breaker. */
1503f6f9f7dSnicm 	if (result == 0)
1513f6f9f7dSnicm 		result = strcmp((*a)->name, (*b)->name);
152a42faf7dSnicm 
1533f6f9f7dSnicm 	if (window_buffer_sort->reversed)
1543f6f9f7dSnicm 		result = -result;
1553f6f9f7dSnicm 	return (result);
156a42faf7dSnicm }
157a42faf7dSnicm 
158a42faf7dSnicm static void
1593f6f9f7dSnicm window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
1603f6f9f7dSnicm     __unused uint64_t *tag, const char *filter)
161a42faf7dSnicm {
162a42faf7dSnicm 	struct window_buffer_modedata	*data = modedata;
163a42faf7dSnicm 	struct window_buffer_itemdata	*item;
164a42faf7dSnicm 	u_int				 i;
165a42faf7dSnicm 	struct paste_buffer		*pb;
166bf38e336Snicm 	char				*text, *cp;
167024c311aSnicm 	struct format_tree		*ft;
16800db7279Snicm 	struct session			*s = NULL;
16900db7279Snicm 	struct winlink			*wl = NULL;
17000db7279Snicm 	struct window_pane		*wp = NULL;
171a42faf7dSnicm 
172a42faf7dSnicm 	for (i = 0; i < data->item_size; i++)
173a42faf7dSnicm 		window_buffer_free_item(data->item_list[i]);
174a42faf7dSnicm 	free(data->item_list);
175a42faf7dSnicm 	data->item_list = NULL;
176a42faf7dSnicm 	data->item_size = 0;
177a42faf7dSnicm 
178a42faf7dSnicm 	pb = NULL;
179a42faf7dSnicm 	while ((pb = paste_walk(pb)) != NULL) {
180a42faf7dSnicm 		item = window_buffer_add_item(data);
181a42faf7dSnicm 		item->name = xstrdup(paste_buffer_name(pb));
182a42faf7dSnicm 		paste_buffer_data(pb, &item->size);
183a42faf7dSnicm 		item->order = paste_buffer_order(pb);
184a42faf7dSnicm 	}
185a42faf7dSnicm 
1863f6f9f7dSnicm 	window_buffer_sort = sort_crit;
187a42faf7dSnicm 	qsort(data->item_list, data->item_size, sizeof *data->item_list,
1883f6f9f7dSnicm 	    window_buffer_cmp);
189a42faf7dSnicm 
19000db7279Snicm 	if (cmd_find_valid_state(&data->fs)) {
19100db7279Snicm 		s = data->fs.s;
19200db7279Snicm 		wl = data->fs.wl;
19300db7279Snicm 		wp = data->fs.wp;
19400db7279Snicm 	}
19500db7279Snicm 
196a42faf7dSnicm 	for (i = 0; i < data->item_size; i++) {
197a42faf7dSnicm 		item = data->item_list[i];
198a42faf7dSnicm 
199024c311aSnicm 		pb = paste_get_name(item->name);
200024c311aSnicm 		if (pb == NULL)
201024c311aSnicm 			continue;
202024c311aSnicm 		ft = format_create(NULL, NULL, FORMAT_NONE, 0);
20300db7279Snicm 		format_defaults(ft, NULL, s, wl, wp);
204024c311aSnicm 		format_defaults_paste_buffer(ft, pb);
205bf38e336Snicm 
206bf38e336Snicm 		if (filter != NULL) {
207024c311aSnicm 			cp = format_expand(ft, filter);
208024c311aSnicm 			if (!format_true(cp)) {
209024c311aSnicm 				free(cp);
210024c311aSnicm 				format_free(ft);
211024c311aSnicm 				continue;
212024c311aSnicm 			}
213024c311aSnicm 			free(cp);
214024c311aSnicm 		}
215024c311aSnicm 
216bf38e336Snicm 		text = format_expand(ft, data->format);
217a42faf7dSnicm 		mode_tree_add(data->data, NULL, item, item->order, item->name,
218a42faf7dSnicm 		    text, -1);
219a42faf7dSnicm 		free(text);
220bf38e336Snicm 
221bf38e336Snicm 		format_free(ft);
222a42faf7dSnicm 	}
223a42faf7dSnicm 
224a42faf7dSnicm }
225a42faf7dSnicm 
2262b7e51f7Snicm static void
2272b7e51f7Snicm window_buffer_draw(__unused void *modedata, void *itemdata,
2282b7e51f7Snicm     struct screen_write_ctx *ctx, u_int sx, u_int sy)
229a42faf7dSnicm {
230a42faf7dSnicm 	struct window_buffer_itemdata	*item = itemdata;
231a42faf7dSnicm 	struct paste_buffer		*pb;
2328917ecadSnicm 	const char			*pdata, *start, *end;
2338917ecadSnicm 	char				*buf = NULL;
234f8ef6c4bSnicm 	size_t				 psize;
2352b7e51f7Snicm 	u_int				 i, cx = ctx->s->cx, cy = ctx->s->cy;
236a42faf7dSnicm 
237a42faf7dSnicm 	pb = paste_get_name(item->name);
238a42faf7dSnicm 	if (pb == NULL)
2392b7e51f7Snicm 		return;
240a42faf7dSnicm 
241a42faf7dSnicm 	pdata = end = paste_buffer_data(pb, &psize);
242a42faf7dSnicm 	for (i = 0; i < sy; i++) {
2438917ecadSnicm 		start = end;
2448917ecadSnicm 		while (end != pdata + psize && *end != '\n')
245a42faf7dSnicm 			end++;
2468917ecadSnicm 		buf = xreallocarray(buf, 4, end - start + 1);
247438eed14Snicm 		utf8_strvis(buf, start, end - start,
248438eed14Snicm 		    VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
2498917ecadSnicm 		if (*buf != '\0') {
2505c6c7001Snicm 			screen_write_cursormove(ctx, cx, cy + i, 0);
2518917ecadSnicm 			screen_write_nputs(ctx, sx, &grid_default_cell, "%s",
2528917ecadSnicm 			    buf);
253a42faf7dSnicm 		}
254a42faf7dSnicm 
255a42faf7dSnicm 		if (end == pdata + psize)
256a42faf7dSnicm 			break;
257a42faf7dSnicm 		end++;
258a42faf7dSnicm 	}
2598917ecadSnicm 	free(buf);
260a42faf7dSnicm }
261a42faf7dSnicm 
262943a08b1Snicm static int
263943a08b1Snicm window_buffer_search(__unused void *modedata, void *itemdata, const char *ss)
264943a08b1Snicm {
265943a08b1Snicm 	struct window_buffer_itemdata	*item = itemdata;
266943a08b1Snicm 	struct paste_buffer		*pb;
267943a08b1Snicm 	const char			*bufdata;
268943a08b1Snicm 	size_t				 bufsize;
269943a08b1Snicm 
270943a08b1Snicm 	if ((pb = paste_get_name(item->name)) == NULL)
271943a08b1Snicm 		return (0);
272943a08b1Snicm 	if (strstr(item->name, ss) != NULL)
2730cf33a8dSnicm 		return (1);
274943a08b1Snicm 	bufdata = paste_buffer_data(pb, &bufsize);
275943a08b1Snicm 	return (memmem(bufdata, bufsize, ss, strlen(ss)) != NULL);
276943a08b1Snicm }
277943a08b1Snicm 
278f43bc87cSnicm static void
279f43bc87cSnicm window_buffer_menu(void *modedata, struct client *c, key_code key)
280f43bc87cSnicm {
281f43bc87cSnicm 	struct window_buffer_modedata	*data = modedata;
282f43bc87cSnicm 	struct window_pane		*wp = data->wp;
283f43bc87cSnicm 	struct window_mode_entry	*wme;
284f43bc87cSnicm 
285f43bc87cSnicm 	wme = TAILQ_FIRST(&wp->modes);
286f43bc87cSnicm 	if (wme == NULL || wme->data != modedata)
287f43bc87cSnicm 		return;
288f43bc87cSnicm 	window_buffer_key(wme, c, NULL, NULL, key, NULL);
289f43bc87cSnicm }
290f43bc87cSnicm 
291438eed14Snicm static key_code
292438eed14Snicm window_buffer_get_key(void *modedata, void *itemdata, u_int line)
293438eed14Snicm {
294438eed14Snicm 	struct window_buffer_modedata	*data = modedata;
295438eed14Snicm 	struct window_buffer_itemdata	*item = itemdata;
296438eed14Snicm 	struct format_tree		*ft;
2972bd728e0Snicm 	struct session			*s = NULL;
2982bd728e0Snicm 	struct winlink			*wl = NULL;
2992bd728e0Snicm 	struct window_pane		*wp = NULL;
300438eed14Snicm 	struct paste_buffer		*pb;
301438eed14Snicm 	char				*expanded;
302438eed14Snicm 	key_code			 key;
303438eed14Snicm 
304438eed14Snicm 	if (cmd_find_valid_state(&data->fs)) {
305438eed14Snicm 		s = data->fs.s;
306438eed14Snicm 		wl = data->fs.wl;
307438eed14Snicm 		wp = data->fs.wp;
308438eed14Snicm 	}
309438eed14Snicm 	pb = paste_get_name(item->name);
310438eed14Snicm 	if (pb == NULL)
3113e8355bdSnicm 		return (KEYC_NONE);
312438eed14Snicm 
313438eed14Snicm 	ft = format_create(NULL, NULL, FORMAT_NONE, 0);
314438eed14Snicm 	format_defaults(ft, NULL, NULL, 0, NULL);
315438eed14Snicm 	format_defaults(ft, NULL, s, wl, wp);
316438eed14Snicm 	format_defaults_paste_buffer(ft, pb);
317438eed14Snicm 	format_add(ft, "line", "%u", line);
318438eed14Snicm 
319438eed14Snicm 	expanded = format_expand(ft, data->key_format);
320438eed14Snicm 	key = key_string_lookup_string(expanded);
321438eed14Snicm 	free(expanded);
322438eed14Snicm 	format_free(ft);
3233e8355bdSnicm 	return (key);
324438eed14Snicm }
325438eed14Snicm 
326a42faf7dSnicm static struct screen *
32700db7279Snicm window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs,
32800db7279Snicm     struct args *args)
329a42faf7dSnicm {
33030a94f45Snicm 	struct window_pane		*wp = wme->wp;
331a42faf7dSnicm 	struct window_buffer_modedata	*data;
332a42faf7dSnicm 	struct screen			*s;
333a42faf7dSnicm 
33430a94f45Snicm 	wme->data = data = xcalloc(1, sizeof *data);
335f43bc87cSnicm 	data->wp = wp;
33600db7279Snicm 	cmd_find_copy_state(&data->fs, fs);
337a42faf7dSnicm 
338bf38e336Snicm 	if (args == NULL || !args_has(args, 'F'))
339bf38e336Snicm 		data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT);
340bf38e336Snicm 	else
341bf38e336Snicm 		data->format = xstrdup(args_get(args, 'F'));
342438eed14Snicm 	if (args == NULL || !args_has(args, 'K'))
343438eed14Snicm 		data->key_format = xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT);
344438eed14Snicm 	else
345438eed14Snicm 		data->key_format = xstrdup(args_get(args, 'K'));
3461693b10bSnicm 	if (args == NULL || args_count(args) == 0)
347a42faf7dSnicm 		data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND);
348a42faf7dSnicm 	else
3491693b10bSnicm 		data->command = xstrdup(args_string(args, 0));
350a42faf7dSnicm 
351b38aa712Snicm 	data->data = mode_tree_start(wp, args, window_buffer_build,
35267c16a7cSnicm 	    window_buffer_draw, window_buffer_search, window_buffer_menu, NULL,
353438eed14Snicm 	    window_buffer_get_key, data, window_buffer_menu_items,
354438eed14Snicm 	    window_buffer_sort_list, nitems(window_buffer_sort_list), &s);
3554f5e4c93Snicm 	mode_tree_zoom(data->data, args);
356a42faf7dSnicm 
357a42faf7dSnicm 	mode_tree_build(data->data);
358a42faf7dSnicm 	mode_tree_draw(data->data);
359a42faf7dSnicm 
360a42faf7dSnicm 	return (s);
361a42faf7dSnicm }
362a42faf7dSnicm 
363a42faf7dSnicm static void
36430a94f45Snicm window_buffer_free(struct window_mode_entry *wme)
365a42faf7dSnicm {
36630a94f45Snicm 	struct window_buffer_modedata	*data = wme->data;
367a42faf7dSnicm 	u_int				 i;
368a42faf7dSnicm 
369a42faf7dSnicm 	if (data == NULL)
370a42faf7dSnicm 		return;
371a42faf7dSnicm 
372a42faf7dSnicm 	mode_tree_free(data->data);
373a42faf7dSnicm 
374a42faf7dSnicm 	for (i = 0; i < data->item_size; i++)
375a42faf7dSnicm 		window_buffer_free_item(data->item_list[i]);
376a42faf7dSnicm 	free(data->item_list);
377a42faf7dSnicm 
378bf38e336Snicm 	free(data->format);
379438eed14Snicm 	free(data->key_format);
380a42faf7dSnicm 	free(data->command);
381bf38e336Snicm 
382a42faf7dSnicm 	free(data);
383a42faf7dSnicm }
384a42faf7dSnicm 
385a42faf7dSnicm static void
38630a94f45Snicm window_buffer_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
387a42faf7dSnicm {
38830a94f45Snicm 	struct window_buffer_modedata	*data = wme->data;
389a42faf7dSnicm 
390a42faf7dSnicm 	mode_tree_resize(data->data, sx, sy);
391a42faf7dSnicm }
392a42faf7dSnicm 
393a42faf7dSnicm static void
394734f37e4Snicm window_buffer_update(struct window_mode_entry *wme)
395734f37e4Snicm {
396734f37e4Snicm 	struct window_buffer_modedata	*data = wme->data;
397734f37e4Snicm 
398734f37e4Snicm 	mode_tree_build(data->data);
399734f37e4Snicm 	mode_tree_draw(data->data);
400734f37e4Snicm 	data->wp->flags |= PANE_REDRAW;
401734f37e4Snicm }
402734f37e4Snicm 
403734f37e4Snicm static void
404d7af2c28Snicm window_buffer_do_delete(void *modedata, void *itemdata,
405d7af2c28Snicm     __unused struct client *c, __unused key_code key)
406a42faf7dSnicm {
407a42faf7dSnicm 	struct window_buffer_modedata	*data = modedata;
408a42faf7dSnicm 	struct window_buffer_itemdata	*item = itemdata;
409a42faf7dSnicm 	struct paste_buffer		*pb;
410a42faf7dSnicm 
411*d3278555Snicm 	if (item == mode_tree_get_current(data->data) &&
412*d3278555Snicm 	    !mode_tree_down(data->data, 0)) {
413*d3278555Snicm 		/*
414*d3278555Snicm 		 *If we were unable to select the item further down we are at
415*d3278555Snicm 		 * the end of the list. Move one element up instead, to make
416*d3278555Snicm 		 * sure that we preserve a valid selection or we risk having
417*d3278555Snicm 		 * the tree build logic reset it to the first item.
418*d3278555Snicm 		 */
419*d3278555Snicm 		mode_tree_up(data->data, 0);
420*d3278555Snicm 	}
421*d3278555Snicm 
422a42faf7dSnicm 	if ((pb = paste_get_name(item->name)) != NULL)
423a42faf7dSnicm 		paste_free(pb);
424a42faf7dSnicm }
425a42faf7dSnicm 
426a42faf7dSnicm static void
427d7af2c28Snicm window_buffer_do_paste(void *modedata, void *itemdata, struct client *c,
428d7af2c28Snicm     __unused key_code key)
429d7af2c28Snicm {
430d7af2c28Snicm 	struct window_buffer_modedata	*data = modedata;
431d7af2c28Snicm 	struct window_buffer_itemdata	*item = itemdata;
432d7af2c28Snicm 
433ba27d7a5Snicm 	if (paste_get_name(item->name) != NULL)
434d7af2c28Snicm 		mode_tree_run_command(c, NULL, data->command, item->name);
435d7af2c28Snicm }
436d7af2c28Snicm 
437d7af2c28Snicm static void
438a6c9106fSnicm window_buffer_finish_edit(struct window_buffer_editdata *ed)
439a6c9106fSnicm {
440a6c9106fSnicm 	free(ed->name);
441a6c9106fSnicm 	free(ed);
442a6c9106fSnicm }
443a6c9106fSnicm 
444a6c9106fSnicm static void
445f67736f8Snicm window_buffer_edit_close_cb(char *buf, size_t len, void *arg)
446a6c9106fSnicm {
447a6c9106fSnicm 	struct window_buffer_editdata	*ed = arg;
448a6c9106fSnicm 	size_t				 oldlen;
449a6c9106fSnicm 	const char			*oldbuf;
450a6c9106fSnicm 	struct paste_buffer		*pb;
451a6c9106fSnicm 	struct window_pane		*wp;
452a6c9106fSnicm 	struct window_buffer_modedata	*data;
453a6c9106fSnicm 	struct window_mode_entry	*wme;
454a6c9106fSnicm 
455f67736f8Snicm 	if (buf == NULL || len == 0) {
456a6c9106fSnicm 		window_buffer_finish_edit(ed);
457a6c9106fSnicm 		return;
458a6c9106fSnicm 	}
459a6c9106fSnicm 
460a6c9106fSnicm 	pb = paste_get_name(ed->name);
461a6c9106fSnicm 	if (pb == NULL || pb != ed->pb) {
462a6c9106fSnicm 		window_buffer_finish_edit(ed);
463a6c9106fSnicm 		return;
464a6c9106fSnicm 	}
465a6c9106fSnicm 
466a6c9106fSnicm 	oldbuf = paste_buffer_data(pb, &oldlen);
467a6c9106fSnicm 	if (oldlen != '\0' &&
468a6c9106fSnicm 	    oldbuf[oldlen - 1] != '\n' &&
469a6c9106fSnicm 	    buf[len - 1] == '\n')
470a6c9106fSnicm 		len--;
471a6c9106fSnicm 	if (len != 0)
472a6c9106fSnicm 		paste_replace(pb, buf, len);
473a6c9106fSnicm 
474a6c9106fSnicm 	wp = window_pane_find_by_id(ed->wp_id);
475a6c9106fSnicm 	if (wp != NULL) {
476a6c9106fSnicm 		wme = TAILQ_FIRST(&wp->modes);
477a6c9106fSnicm 		if (wme->mode == &window_buffer_mode) {
478a6c9106fSnicm 			data = wme->data;
479a6c9106fSnicm 			mode_tree_build(data->data);
480a6c9106fSnicm 			mode_tree_draw(data->data);
481a6c9106fSnicm 		}
482a6c9106fSnicm 		wp->flags |= PANE_REDRAW;
483a6c9106fSnicm 	}
484a6c9106fSnicm 	window_buffer_finish_edit(ed);
485a6c9106fSnicm }
486a6c9106fSnicm 
487a6c9106fSnicm static void
488a6c9106fSnicm window_buffer_start_edit(struct window_buffer_modedata *data,
489a6c9106fSnicm     struct window_buffer_itemdata *item, struct client *c)
490a6c9106fSnicm {
491a6c9106fSnicm 	struct paste_buffer		*pb;
492a6c9106fSnicm 	const char			*buf;
493a6c9106fSnicm 	size_t				 len;
494a6c9106fSnicm 	struct window_buffer_editdata	*ed;
495a6c9106fSnicm 
496a6c9106fSnicm 	if ((pb = paste_get_name(item->name)) == NULL)
497a6c9106fSnicm 		return;
498a6c9106fSnicm 	buf = paste_buffer_data(pb, &len);
499a6c9106fSnicm 
500a6c9106fSnicm 	ed = xcalloc(1, sizeof *ed);
501a6c9106fSnicm 	ed->wp_id = data->wp->id;
502a6c9106fSnicm 	ed->name = xstrdup(paste_buffer_name(pb));
503a6c9106fSnicm 	ed->pb = pb;
504a6c9106fSnicm 
505f67736f8Snicm 	if (popup_editor(c, buf, len, window_buffer_edit_close_cb, ed) != 0)
506a6c9106fSnicm 		window_buffer_finish_edit(ed);
507a6c9106fSnicm }
508a6c9106fSnicm 
509a6c9106fSnicm static void
51030a94f45Snicm window_buffer_key(struct window_mode_entry *wme, struct client *c,
511bf52409eSnicm     __unused struct session *s, __unused struct winlink *wl, key_code key,
512bf52409eSnicm     struct mouse_event *m)
513a42faf7dSnicm {
51430a94f45Snicm 	struct window_pane		*wp = wme->wp;
51530a94f45Snicm 	struct window_buffer_modedata	*data = wme->data;
516d7af2c28Snicm 	struct mode_tree_data		*mtd = data->data;
517a42faf7dSnicm 	struct window_buffer_itemdata	*item;
518a42faf7dSnicm 	int				 finished;
519a42faf7dSnicm 
52000b011bcSnicm 	if (paste_is_empty()) {
521ee191f7aSnicm 		finished = 1;
522ee191f7aSnicm 		goto out;
523ee191f7aSnicm 	}
524ee191f7aSnicm 
52563c9949dSnicm 	finished = mode_tree_key(mtd, c, &key, m, NULL, NULL);
526a42faf7dSnicm 	switch (key) {
527a6c9106fSnicm 	case 'e':
528a6c9106fSnicm 		item = mode_tree_get_current(mtd);
529a6c9106fSnicm 		window_buffer_start_edit(data, item, c);
530a6c9106fSnicm 		break;
531a42faf7dSnicm 	case 'd':
532d7af2c28Snicm 		item = mode_tree_get_current(mtd);
533d7af2c28Snicm 		window_buffer_do_delete(data, item, c, key);
534d7af2c28Snicm 		mode_tree_build(mtd);
535a42faf7dSnicm 		break;
536a42faf7dSnicm 	case 'D':
537d7af2c28Snicm 		mode_tree_each_tagged(mtd, window_buffer_do_delete, c, key, 0);
538d7af2c28Snicm 		mode_tree_build(mtd);
539a42faf7dSnicm 		break;
540d7af2c28Snicm 	case 'P':
541d7af2c28Snicm 		mode_tree_each_tagged(mtd, window_buffer_do_paste, c, key, 0);
542d7af2c28Snicm 		finished = 1;
543d7af2c28Snicm 		break;
544d7af2c28Snicm 	case 'p':
545a42faf7dSnicm 	case '\r':
546d7af2c28Snicm 		item = mode_tree_get_current(mtd);
547d7af2c28Snicm 		window_buffer_do_paste(data, item, c, key);
548d7af2c28Snicm 		finished = 1;
549d7af2c28Snicm 		break;
550a42faf7dSnicm 	}
551ee191f7aSnicm 
552ee191f7aSnicm out:
55300b011bcSnicm 	if (finished || paste_is_empty())
554a42faf7dSnicm 		window_pane_reset_mode(wp);
555a42faf7dSnicm 	else {
556d7af2c28Snicm 		mode_tree_draw(mtd);
557a42faf7dSnicm 		wp->flags |= PANE_REDRAW;
558a42faf7dSnicm 	}
559a42faf7dSnicm }
560