xref: /openbsd/usr.bin/tmux/window-copy.c (revision 4d3ac07a)
1 /* $OpenBSD: window-copy.c,v 1.368 2024/11/26 15:52:41 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 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 <ctype.h>
22 #include <regex.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 
27 #include "tmux.h"
28 
29 struct window_copy_mode_data;
30 
31 static const char *window_copy_key_table(struct window_mode_entry *);
32 static void	window_copy_command(struct window_mode_entry *, struct client *,
33 		    struct session *, struct winlink *, struct args *,
34 		    struct mouse_event *);
35 static struct screen *window_copy_init(struct window_mode_entry *,
36 		    struct cmd_find_state *, struct args *);
37 static struct screen *window_copy_view_init(struct window_mode_entry *,
38 		    struct cmd_find_state *, struct args *);
39 static void	window_copy_free(struct window_mode_entry *);
40 static void	window_copy_resize(struct window_mode_entry *, u_int, u_int);
41 static void	window_copy_formats(struct window_mode_entry *,
42 		    struct format_tree *);
43 static void	window_copy_scroll1(struct window_mode_entry *,
44 		    struct window_pane *wp, int, u_int, int);
45 static void	window_copy_pageup1(struct window_mode_entry *, int);
46 static int	window_copy_pagedown1(struct window_mode_entry *, int, int);
47 static void	window_copy_next_paragraph(struct window_mode_entry *);
48 static void	window_copy_previous_paragraph(struct window_mode_entry *);
49 static void	window_copy_redraw_selection(struct window_mode_entry *, u_int);
50 static void	window_copy_redraw_lines(struct window_mode_entry *, u_int,
51 		    u_int);
52 static void	window_copy_redraw_screen(struct window_mode_entry *);
53 static void	window_copy_write_line(struct window_mode_entry *,
54 		    struct screen_write_ctx *, u_int);
55 static void	window_copy_write_lines(struct window_mode_entry *,
56 		    struct screen_write_ctx *, u_int, u_int);
57 static char    *window_copy_match_at_cursor(struct window_copy_mode_data *);
58 static void	window_copy_scroll_to(struct window_mode_entry *, u_int, u_int,
59 		    int);
60 static int	window_copy_search_compare(struct grid *, u_int, u_int,
61 		    struct grid *, u_int, int);
62 static int	window_copy_search_lr(struct grid *, struct grid *, u_int *,
63 		    u_int, u_int, u_int, int);
64 static int	window_copy_search_rl(struct grid *, struct grid *, u_int *,
65 		    u_int, u_int, u_int, int);
66 static int	window_copy_last_regex(struct grid *, u_int, u_int, u_int,
67 		    u_int, u_int *, u_int *, const char *, const regex_t *,
68 		    int);
69 static int	window_copy_search_mark_at(struct window_copy_mode_data *,
70 		    u_int, u_int, u_int *);
71 static char    *window_copy_stringify(struct grid *, u_int, u_int, u_int,
72 		    char *, u_int *);
73 static void	window_copy_cstrtocellpos(struct grid *, u_int, u_int *,
74 		    u_int *, const char *);
75 static int	window_copy_search_marks(struct window_mode_entry *,
76 		    struct screen *, int, int);
77 static void	window_copy_clear_marks(struct window_mode_entry *);
78 static int	window_copy_is_lowercase(const char *);
79 static void	window_copy_search_back_overlap(struct grid *, regex_t *,
80 		    u_int *, u_int *, u_int *, u_int);
81 static int	window_copy_search_jump(struct window_mode_entry *,
82 		    struct grid *, struct grid *, u_int, u_int, u_int, int, int,
83 		    int, int);
84 static int	window_copy_search(struct window_mode_entry *, int, int);
85 static int	window_copy_search_up(struct window_mode_entry *, int);
86 static int	window_copy_search_down(struct window_mode_entry *, int);
87 static void	window_copy_goto_line(struct window_mode_entry *, const char *);
88 static void	window_copy_update_cursor(struct window_mode_entry *, u_int,
89 		    u_int);
90 static void	window_copy_start_selection(struct window_mode_entry *);
91 static int	window_copy_adjust_selection(struct window_mode_entry *,
92 		    u_int *, u_int *);
93 static int	window_copy_set_selection(struct window_mode_entry *, int, int);
94 static int	window_copy_update_selection(struct window_mode_entry *, int,
95 		    int);
96 static void	window_copy_synchronize_cursor(struct window_mode_entry *, int);
97 static void    *window_copy_get_selection(struct window_mode_entry *, size_t *);
98 static void	window_copy_copy_buffer(struct window_mode_entry *,
99 		    const char *, void *, size_t, int, int);
100 static void	window_copy_pipe(struct window_mode_entry *,
101 		    struct session *, const char *);
102 static void	window_copy_copy_pipe(struct window_mode_entry *,
103 		    struct session *, const char *, const char *,
104 		    int, int);
105 static void	window_copy_copy_selection(struct window_mode_entry *,
106 		    const char *, int, int);
107 static void	window_copy_append_selection(struct window_mode_entry *);
108 static void	window_copy_clear_selection(struct window_mode_entry *);
109 static void	window_copy_copy_line(struct window_mode_entry *, char **,
110 		    size_t *, u_int, u_int, u_int);
111 static int	window_copy_in_set(struct window_mode_entry *, u_int, u_int,
112 		    const char *);
113 static u_int	window_copy_find_length(struct window_mode_entry *, u_int);
114 static void	window_copy_cursor_start_of_line(struct window_mode_entry *);
115 static void	window_copy_cursor_back_to_indentation(
116 		    struct window_mode_entry *);
117 static void	window_copy_cursor_end_of_line(struct window_mode_entry *);
118 static void	window_copy_other_end(struct window_mode_entry *);
119 static void	window_copy_cursor_left(struct window_mode_entry *);
120 static void	window_copy_cursor_right(struct window_mode_entry *, int);
121 static void	window_copy_cursor_up(struct window_mode_entry *, int);
122 static void	window_copy_cursor_down(struct window_mode_entry *, int);
123 static void	window_copy_cursor_jump(struct window_mode_entry *);
124 static void	window_copy_cursor_jump_back(struct window_mode_entry *);
125 static void	window_copy_cursor_jump_to(struct window_mode_entry *);
126 static void	window_copy_cursor_jump_to_back(struct window_mode_entry *);
127 static void	window_copy_cursor_next_word(struct window_mode_entry *,
128 		    const char *);
129 static void	window_copy_cursor_next_word_end_pos(struct window_mode_entry *,
130 		    const char *, u_int *, u_int *);
131 static void	window_copy_cursor_next_word_end(struct window_mode_entry *,
132 		    const char *, int);
133 static void	window_copy_cursor_previous_word_pos(struct window_mode_entry *,
134 		    const char *, u_int *, u_int *);
135 static void	window_copy_cursor_previous_word(struct window_mode_entry *,
136 		    const char *, int);
137 static void	window_copy_cursor_prompt(struct window_mode_entry *, int,
138 		    int);
139 static void	window_copy_scroll_up(struct window_mode_entry *, u_int);
140 static void	window_copy_scroll_down(struct window_mode_entry *, u_int);
141 static void	window_copy_rectangle_set(struct window_mode_entry *, int);
142 static void	window_copy_move_mouse(struct mouse_event *);
143 static void	window_copy_drag_update(struct client *, struct mouse_event *);
144 static void	window_copy_drag_release(struct client *, struct mouse_event *);
145 static void	window_copy_jump_to_mark(struct window_mode_entry *);
146 static void	window_copy_acquire_cursor_up(struct window_mode_entry *,
147 		    u_int, u_int, u_int, u_int, u_int);
148 static void	window_copy_acquire_cursor_down(struct window_mode_entry *,
149 		    u_int, u_int, u_int, u_int, u_int, u_int, int);
150 static u_int	window_copy_clip_width(u_int, u_int, u_int, u_int);
151 static u_int	window_copy_search_mark_match(struct window_copy_mode_data *,
152 		    u_int , u_int, u_int, int);
153 
154 const struct window_mode window_copy_mode = {
155 	.name = "copy-mode",
156 
157 	.init = window_copy_init,
158 	.free = window_copy_free,
159 	.resize = window_copy_resize,
160 	.key_table = window_copy_key_table,
161 	.command = window_copy_command,
162 	.formats = window_copy_formats,
163 };
164 
165 const struct window_mode window_view_mode = {
166 	.name = "view-mode",
167 
168 	.init = window_copy_view_init,
169 	.free = window_copy_free,
170 	.resize = window_copy_resize,
171 	.key_table = window_copy_key_table,
172 	.command = window_copy_command,
173 	.formats = window_copy_formats,
174 };
175 
176 enum {
177 	WINDOW_COPY_OFF,
178 	WINDOW_COPY_SEARCHUP,
179 	WINDOW_COPY_SEARCHDOWN,
180 	WINDOW_COPY_JUMPFORWARD,
181 	WINDOW_COPY_JUMPBACKWARD,
182 	WINDOW_COPY_JUMPTOFORWARD,
183 	WINDOW_COPY_JUMPTOBACKWARD,
184 };
185 
186 enum {
187 	WINDOW_COPY_REL_POS_ABOVE,
188 	WINDOW_COPY_REL_POS_ON_SCREEN,
189 	WINDOW_COPY_REL_POS_BELOW,
190 };
191 
192 enum window_copy_cmd_action {
193 	WINDOW_COPY_CMD_NOTHING,
194 	WINDOW_COPY_CMD_REDRAW,
195 	WINDOW_COPY_CMD_CANCEL,
196 };
197 
198 enum window_copy_cmd_clear {
199 	WINDOW_COPY_CMD_CLEAR_ALWAYS,
200 	WINDOW_COPY_CMD_CLEAR_NEVER,
201 	WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
202 };
203 
204 struct window_copy_cmd_state {
205 	struct window_mode_entry	*wme;
206 	struct args			*args;
207 	struct args			*wargs;
208 	struct mouse_event		*m;
209 
210 	struct client			*c;
211 	struct session			*s;
212 	struct winlink			*wl;
213 };
214 
215 /*
216  * Copy mode's visible screen (the "screen" field) is filled from one of two
217  * sources: the original contents of the pane (used when we actually enter via
218  * the "copy-mode" command, to copy the contents of the current pane), or else
219  * a series of lines containing the output from an output-writing tmux command
220  * (such as any of the "show-*" or "list-*" commands).
221  *
222  * In either case, the full content of the copy-mode grid is pointed at by the
223  * "backing" field, and is copied into "screen" as needed (that is, when
224  * scrolling occurs). When copy-mode is backed by a pane, backing points
225  * directly at that pane's screen structure (&wp->base); when backed by a list
226  * of output-lines from a command, it points at a newly-allocated screen
227  * structure (which is deallocated when the mode ends).
228  */
229 struct window_copy_mode_data {
230 	struct screen	 screen;
231 
232 	struct screen	*backing;
233 	int		 backing_written; /* backing display started */
234 	struct screen	*writing;
235 	struct input_ctx *ictx;
236 
237 	int		 viewmode;	/* view mode entered */
238 
239 	u_int		 oy;		/* number of lines scrolled up */
240 
241 	u_int		 selx;		/* beginning of selection */
242 	u_int		 sely;
243 
244 	u_int		 endselx;	/* end of selection */
245 	u_int		 endsely;
246 
247 	enum {
248 		CURSORDRAG_NONE,	/* selection is independent of cursor */
249 		CURSORDRAG_ENDSEL,	/* end is synchronized with cursor */
250 		CURSORDRAG_SEL,		/* start is synchronized with cursor */
251 	} cursordrag;
252 
253 	int		 modekeys;
254 	enum {
255 		LINE_SEL_NONE,
256 		LINE_SEL_LEFT_RIGHT,
257 		LINE_SEL_RIGHT_LEFT,
258 	} lineflag;			/* line selection mode */
259 	int		 rectflag;	/* in rectangle copy mode? */
260 	int		 scroll_exit;	/* exit on scroll to end? */
261 	int		 hide_position;	/* hide position marker */
262 
263 	enum {
264 		SEL_CHAR,		/* select one char at a time */
265 		SEL_WORD,		/* select one word at a time */
266 		SEL_LINE,		/* select one line at a time */
267 	} selflag;
268 
269 	const char	*separators;	/* word separators */
270 
271 	u_int		 dx;		/* drag start position */
272 	u_int		 dy;
273 
274 	u_int		 selrx;		/* selection reset positions */
275 	u_int		 selry;
276 	u_int		 endselrx;
277 	u_int		 endselry;
278 
279 	u_int		 cx;
280 	u_int		 cy;
281 
282 	u_int		 lastcx; 	/* position in last line w/ content */
283 	u_int		 lastsx;	/* size of last line w/ content */
284 
285 	u_int		 mx;		/* mark position */
286 	u_int		 my;
287 	int		 showmark;
288 
289 	int		 searchtype;
290 	int		 searchdirection;
291 	int		 searchregex;
292 	char		*searchstr;
293 	u_char		*searchmark;
294 	int		 searchcount;
295 	int		 searchmore;
296 	int		 searchall;
297 	int		 searchx;
298 	int		 searchy;
299 	int		 searcho;
300 	u_char		 searchgen;
301 
302 	int		 timeout;	/* search has timed out */
303 #define WINDOW_COPY_SEARCH_TIMEOUT 10000
304 #define WINDOW_COPY_SEARCH_ALL_TIMEOUT 200
305 #define WINDOW_COPY_SEARCH_MAX_LINE 2000
306 
307 	int			 jumptype;
308 	struct utf8_data	*jumpchar;
309 
310 	struct event	 dragtimer;
311 #define WINDOW_COPY_DRAG_REPEAT_TIME 50000
312 };
313 
314 static void
window_copy_scroll_timer(__unused int fd,__unused short events,void * arg)315 window_copy_scroll_timer(__unused int fd, __unused short events, void *arg)
316 {
317 	struct window_mode_entry	*wme = arg;
318 	struct window_pane		*wp = wme->wp;
319 	struct window_copy_mode_data	*data = wme->data;
320 	struct timeval			 tv = {
321 		.tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
322 	};
323 
324 	evtimer_del(&data->dragtimer);
325 
326 	if (TAILQ_FIRST(&wp->modes) != wme)
327 		return;
328 
329 	if (data->cy == 0) {
330 		evtimer_add(&data->dragtimer, &tv);
331 		window_copy_cursor_up(wme, 1);
332 	} else if (data->cy == screen_size_y(&data->screen) - 1) {
333 		evtimer_add(&data->dragtimer, &tv);
334 		window_copy_cursor_down(wme, 1);
335 	}
336 }
337 
338 static struct screen *
window_copy_clone_screen(struct screen * src,struct screen * hint,u_int * cx,u_int * cy,int trim)339 window_copy_clone_screen(struct screen *src, struct screen *hint, u_int *cx,
340     u_int *cy, int trim)
341 {
342 	struct screen		*dst;
343 	const struct grid_line	*gl;
344 	u_int			 sy, wx, wy;
345 	int			 reflow;
346 
347 	dst = xcalloc(1, sizeof *dst);
348 
349 	sy = screen_hsize(src) + screen_size_y(src);
350 	if (trim) {
351 		while (sy > screen_hsize(src)) {
352 			gl = grid_peek_line(src->grid, sy - 1);
353 			if (gl->cellused != 0)
354 				break;
355 			sy--;
356 		}
357 	}
358 	log_debug("%s: target screen is %ux%u, source %ux%u", __func__,
359 	    screen_size_x(src), sy, screen_size_x(hint),
360 	    screen_hsize(src) + screen_size_y(src));
361 	screen_init(dst, screen_size_x(src), sy, screen_hlimit(src));
362 
363 	/*
364 	 * Ensure history is on for the backing grid so lines are not deleted
365 	 * during resizing.
366 	 */
367 	dst->grid->flags |= GRID_HISTORY;
368 	grid_duplicate_lines(dst->grid, 0, src->grid, 0, sy);
369 
370 	dst->grid->sy = sy - screen_hsize(src);
371 	dst->grid->hsize = screen_hsize(src);
372 	dst->grid->hscrolled = src->grid->hscrolled;
373 	if (src->cy > dst->grid->sy - 1) {
374 		dst->cx = 0;
375 		dst->cy = dst->grid->sy - 1;
376 	} else {
377 		dst->cx = src->cx;
378 		dst->cy = src->cy;
379 	}
380 
381 	if (cx != NULL && cy != NULL) {
382 		*cx = dst->cx;
383 		*cy = screen_hsize(dst) + dst->cy;
384 		reflow = (screen_size_x(hint) != screen_size_x(dst));
385 	}
386 	else
387 		reflow = 0;
388 	if (reflow)
389 		grid_wrap_position(dst->grid, *cx, *cy, &wx, &wy);
390 	screen_resize_cursor(dst, screen_size_x(hint), screen_size_y(hint), 1,
391 	    0, 0);
392 	if (reflow)
393 		grid_unwrap_position(dst->grid, cx, cy, wx, wy);
394 
395 	return (dst);
396 }
397 
398 static struct window_copy_mode_data *
window_copy_common_init(struct window_mode_entry * wme)399 window_copy_common_init(struct window_mode_entry *wme)
400 {
401 	struct window_pane		*wp = wme->wp;
402 	struct window_copy_mode_data	*data;
403 	struct screen			*base = &wp->base;
404 
405 	wme->data = data = xcalloc(1, sizeof *data);
406 
407 	data->cursordrag = CURSORDRAG_NONE;
408 	data->lineflag = LINE_SEL_NONE;
409 	data->selflag = SEL_CHAR;
410 
411 	if (wp->searchstr != NULL) {
412 		data->searchtype = WINDOW_COPY_SEARCHUP;
413 		data->searchregex = wp->searchregex;
414 		data->searchstr = xstrdup(wp->searchstr);
415 	} else {
416 		data->searchtype = WINDOW_COPY_OFF;
417 		data->searchregex = 0;
418 		data->searchstr = NULL;
419 	}
420 	data->searchx = data->searchy = data->searcho = -1;
421 	data->searchall = 1;
422 
423 	data->jumptype = WINDOW_COPY_OFF;
424 	data->jumpchar = NULL;
425 
426 	screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0);
427 	screen_set_default_cursor(&data->screen, global_w_options);
428 	data->modekeys = options_get_number(wp->window->options, "mode-keys");
429 
430 	evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme);
431 
432 	return (data);
433 }
434 
435 static struct screen *
window_copy_init(struct window_mode_entry * wme,__unused struct cmd_find_state * fs,struct args * args)436 window_copy_init(struct window_mode_entry *wme,
437     __unused struct cmd_find_state *fs, struct args *args)
438 {
439 	struct window_pane		*wp = wme->swp;
440 	struct window_copy_mode_data	*data;
441 	struct screen			*base = &wp->base;
442 	struct screen_write_ctx		 ctx;
443 	u_int				 i, cx, cy;
444 
445 	data = window_copy_common_init(wme);
446 	data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy,
447 	    wme->swp != wme->wp);
448 
449 	data->cx = cx;
450 	if (cy < screen_hsize(data->backing)) {
451 		data->cy = 0;
452 		data->oy = screen_hsize(data->backing) - cy;
453 	} else {
454 		data->cy = cy - screen_hsize(data->backing);
455 		data->oy = 0;
456 	}
457 
458 	data->scroll_exit = args_has(args, 'e');
459 	data->hide_position = args_has(args, 'H');
460 
461 	if (base->hyperlinks != NULL)
462 		data->screen.hyperlinks = hyperlinks_copy(base->hyperlinks);
463 	data->screen.cx = data->cx;
464 	data->screen.cy = data->cy;
465 	data->mx = data->cx;
466 	data->my = screen_hsize(data->backing) + data->cy - data->oy;
467 	data->showmark = 0;
468 
469 	screen_write_start(&ctx, &data->screen);
470 	for (i = 0; i < screen_size_y(&data->screen); i++)
471 		window_copy_write_line(wme, &ctx, i);
472 	screen_write_cursormove(&ctx, data->cx, data->cy, 0);
473 	screen_write_stop(&ctx);
474 
475 	return (&data->screen);
476 }
477 
478 static struct screen *
window_copy_view_init(struct window_mode_entry * wme,__unused struct cmd_find_state * fs,__unused struct args * args)479 window_copy_view_init(struct window_mode_entry *wme,
480     __unused struct cmd_find_state *fs, __unused struct args *args)
481 {
482 	struct window_pane		*wp = wme->wp;
483 	struct window_copy_mode_data	*data;
484 	struct screen			*base = &wp->base;
485 	u_int				 sx = screen_size_x(base);
486 
487 	data = window_copy_common_init(wme);
488 	data->viewmode = 1;
489 
490 	data->backing = xmalloc(sizeof *data->backing);
491 	screen_init(data->backing, sx, screen_size_y(base), UINT_MAX);
492 	data->writing = xmalloc(sizeof *data->writing);
493 	screen_init(data->writing, sx, screen_size_y(base), 0);
494 	data->ictx = input_init(NULL, NULL, NULL);
495 	data->mx = data->cx;
496 	data->my = screen_hsize(data->backing) + data->cy - data->oy;
497 	data->showmark = 0;
498 
499 	return (&data->screen);
500 }
501 
502 static void
window_copy_free(struct window_mode_entry * wme)503 window_copy_free(struct window_mode_entry *wme)
504 {
505 	struct window_copy_mode_data	*data = wme->data;
506 
507 	evtimer_del(&data->dragtimer);
508 
509 	free(data->searchmark);
510 	free(data->searchstr);
511 	free(data->jumpchar);
512 
513 	if (data->writing != NULL) {
514 		screen_free(data->writing);
515 		free(data->writing);
516 	}
517 	if (data->ictx != NULL)
518 		input_free(data->ictx);
519 	screen_free(data->backing);
520 	free(data->backing);
521 
522 	screen_free(&data->screen);
523 	free(data);
524 }
525 
526 void
window_copy_add(struct window_pane * wp,int parse,const char * fmt,...)527 window_copy_add(struct window_pane *wp, int parse, const char *fmt, ...)
528 {
529 	va_list	ap;
530 
531 	va_start(ap, fmt);
532 	window_copy_vadd(wp, parse, fmt, ap);
533 	va_end(ap);
534 }
535 
536 static void
window_copy_init_ctx_cb(__unused struct screen_write_ctx * ctx,struct tty_ctx * ttyctx)537 window_copy_init_ctx_cb(__unused struct screen_write_ctx *ctx,
538     struct tty_ctx *ttyctx)
539 {
540 	memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
541 	ttyctx->palette = NULL;
542 	ttyctx->redraw_cb = NULL;
543 	ttyctx->set_client_cb = NULL;
544 	ttyctx->arg = NULL;
545 }
546 
547 void
window_copy_vadd(struct window_pane * wp,int parse,const char * fmt,va_list ap)548 window_copy_vadd(struct window_pane *wp, int parse, const char *fmt, va_list ap)
549 {
550 	struct window_mode_entry	*wme = TAILQ_FIRST(&wp->modes);
551 	struct window_copy_mode_data	*data = wme->data;
552 	struct screen			*backing = data->backing;
553 	struct screen			*writing = data->writing;
554 	struct screen_write_ctx	 	 writing_ctx, backing_ctx, ctx;
555 	struct grid_cell		 gc;
556 	u_int				 old_hsize, old_cy;
557 	u_int				 sx = screen_size_x(backing);
558 	char				*text;
559 
560 	if (parse) {
561 		vasprintf(&text, fmt, ap);
562 		screen_write_start(&writing_ctx, writing);
563 		screen_write_reset(&writing_ctx);
564 		input_parse_screen(data->ictx, writing, window_copy_init_ctx_cb,
565 		    data, text, strlen(text));
566 		free(text);
567 	}
568 
569 	old_hsize = screen_hsize(data->backing);
570 	screen_write_start(&backing_ctx, backing);
571 	if (data->backing_written) {
572 		/*
573 		 * On the second or later line, do a CRLF before writing
574 		 * (so it's on a new line).
575 		 */
576 		screen_write_carriagereturn(&backing_ctx);
577 		screen_write_linefeed(&backing_ctx, 0, 8);
578 	} else
579 		data->backing_written = 1;
580 	old_cy = backing->cy;
581 	if (parse)
582 		screen_write_fast_copy(&backing_ctx, writing, 0, 0, sx, 1);
583 	else {
584 		memcpy(&gc, &grid_default_cell, sizeof gc);
585 		screen_write_vnputs(&backing_ctx, 0, &gc, fmt, ap);
586 	}
587 	screen_write_stop(&backing_ctx);
588 
589 	data->oy += screen_hsize(data->backing) - old_hsize;
590 
591 	screen_write_start_pane(&ctx, wp, &data->screen);
592 
593 	/*
594 	 * If the history has changed, draw the top line.
595 	 * (If there's any history at all, it has changed.)
596 	 */
597 	if (screen_hsize(data->backing))
598 		window_copy_redraw_lines(wme, 0, 1);
599 
600 	/* Write the new lines. */
601 	window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1);
602 
603 	screen_write_stop(&ctx);
604 }
605 
606 void
window_copy_scroll(struct window_pane * wp,int sl_mpos,u_int my,int scroll_exit)607 window_copy_scroll(struct window_pane *wp, int sl_mpos, u_int my,
608     int scroll_exit)
609 {
610 	struct window_mode_entry	*wme = TAILQ_FIRST(&wp->modes);
611 
612 	if (wme != NULL) {
613 		window_set_active_pane(wp->window, wp, 0);
614 		window_copy_scroll1(wme, wp, sl_mpos, my, scroll_exit);
615 	}
616 }
617 
618 static void
window_copy_scroll1(struct window_mode_entry * wme,struct window_pane * wp,int sl_mpos,u_int my,int scroll_exit)619 window_copy_scroll1(struct window_mode_entry *wme, struct window_pane *wp,
620     int sl_mpos, u_int my, int scroll_exit)
621 {
622 	struct window_copy_mode_data	*data = wme->data;
623 	u_int				 ox, oy, px, py, n, offset, size;
624 	u_int				 new_offset;
625 	u_int				 slider_height = wp->sb_slider_h;
626 	u_int				 sb_height = wp->sy, sb_top = wp->yoff;
627 	u_int				 sy = screen_size_y(data->backing);
628 	int				 new_slider_y, delta;
629 
630 	/*
631 	 * sl_mpos is where in the slider the user is dragging, mouse is
632 	 * dragging this y point relative to top of slider.
633 	 */
634 	if (my <= sb_top + sl_mpos) {
635 		/* Slider banged into top. */
636 		new_slider_y = sb_top - wp->yoff;
637 	} else if (my - sl_mpos > sb_top + sb_height - slider_height) {
638 		/* Slider banged into bottom. */
639 		new_slider_y = sb_top - wp->yoff + (sb_height - slider_height);
640 	} else {
641 		/* Slider is somewhere in the middle. */
642 		new_slider_y = my - wp->yoff - sl_mpos + 1;
643 	}
644 
645 	if (TAILQ_FIRST(&wp->modes) == NULL ||
646 	    window_copy_get_current_offset(wp, &offset, &size) == 0)
647 		return;
648 
649 	/*
650 	 * See screen_redraw_draw_pane_scrollbar - this is the inverse of the
651 	 * formula used there.
652 	 */
653 	new_offset = new_slider_y * ((float)(size + sb_height) / sb_height);
654 	delta = (int)offset - new_offset;
655 
656 	/*
657 	 * Move pane view around based on delta relative to the cursor,
658 	 * maintaining the selection.
659 	 */
660 	oy = screen_hsize(data->backing) + data->cy - data->oy;
661 	ox = window_copy_find_length(wme, oy);
662 
663 	if (data->cx != ox) {
664 		data->lastcx = data->cx;
665 		data->lastsx = ox;
666 	}
667 	data->cx = data->lastcx;
668 
669 	if (delta >= 0) {
670 		n = (u_int)delta;
671 		if (data->oy + n > screen_hsize(data->backing)) {
672 			data->oy = screen_hsize(data->backing);
673 			if (data->cy < n)
674 				data->cy = 0;
675 			else
676 				data->cy -= n;
677 		} else
678 			data->oy += n;
679 	} else {
680 		n = (u_int)-delta;
681 		if (data->oy < n) {
682 			data->oy = 0;
683 			if (data->cy + (n - data->oy) >= sy)
684 				data->cy = sy - 1;
685 			else
686 				data->cy += n - data->oy;
687 		} else
688 			data->oy -= n;
689 	}
690 
691 	/* Don't also drag tail when dragging a scrollbar, it looks weird. */
692 	data->cursordrag = CURSORDRAG_NONE;
693 
694 	if (data->screen.sel == NULL || !data->rectflag) {
695 		py = screen_hsize(data->backing) + data->cy - data->oy;
696 		px = window_copy_find_length(wme, py);
697 		if ((data->cx >= data->lastsx && data->cx != px) ||
698 		    data->cx > px)
699 			window_copy_cursor_end_of_line(wme);
700 	}
701 
702 	if (scroll_exit && data->oy == 0) {
703 		window_pane_reset_mode(wp);
704 		return;
705 	}
706 
707 	if (data->searchmark != NULL && !data->timeout)
708 		window_copy_search_marks(wme, NULL, data->searchregex, 1);
709 	window_copy_update_selection(wme, 1, 0);
710 	window_copy_redraw_screen(wme);
711 }
712 
713 void
window_copy_pageup(struct window_pane * wp,int half_page)714 window_copy_pageup(struct window_pane *wp, int half_page)
715 {
716 	window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page);
717 }
718 
719 static void
window_copy_pageup1(struct window_mode_entry * wme,int half_page)720 window_copy_pageup1(struct window_mode_entry *wme, int half_page)
721 {
722 	struct window_copy_mode_data	*data = wme->data;
723 	struct screen			*s = &data->screen;
724 	u_int				 n, ox, oy, px, py;
725 
726 	oy = screen_hsize(data->backing) + data->cy - data->oy;
727 	ox = window_copy_find_length(wme, oy);
728 
729 	if (data->cx != ox) {
730 		data->lastcx = data->cx;
731 		data->lastsx = ox;
732 	}
733 	data->cx = data->lastcx;
734 
735 	n = 1;
736 	if (screen_size_y(s) > 2) {
737 		if (half_page)
738 			n = screen_size_y(s) / 2;
739 		else
740 			n = screen_size_y(s) - 2;
741 	}
742 
743 	if (data->oy + n > screen_hsize(data->backing)) {
744 		data->oy = screen_hsize(data->backing);
745 		if (data->cy < n)
746 			data->cy = 0;
747 		else
748 			data->cy -= n;
749 	} else
750 		data->oy += n;
751 
752 	if (data->screen.sel == NULL || !data->rectflag) {
753 		py = screen_hsize(data->backing) + data->cy - data->oy;
754 		px = window_copy_find_length(wme, py);
755 		if ((data->cx >= data->lastsx && data->cx != px) ||
756 		    data->cx > px)
757 			window_copy_cursor_end_of_line(wme);
758 	}
759 
760 	if (data->searchmark != NULL && !data->timeout)
761 		window_copy_search_marks(wme, NULL, data->searchregex, 1);
762 	window_copy_update_selection(wme, 1, 0);
763 	window_copy_redraw_screen(wme);
764 }
765 
766 void
window_copy_pagedown(struct window_pane * wp,int half_page,int scroll_exit)767 window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit)
768 {
769 	if (window_copy_pagedown1(TAILQ_FIRST(&wp->modes), half_page,
770 	    scroll_exit)) {
771 		window_pane_reset_mode(wp);
772 		return;
773 	}
774 }
775 
776 static int
window_copy_pagedown1(struct window_mode_entry * wme,int half_page,int scroll_exit)777 window_copy_pagedown1(struct window_mode_entry *wme, int half_page,
778     int scroll_exit)
779 {
780 	struct window_copy_mode_data	*data = wme->data;
781 	struct screen			*s = &data->screen;
782 	u_int				 n, ox, oy, px, py;
783 
784 	oy = screen_hsize(data->backing) + data->cy - data->oy;
785 	ox = window_copy_find_length(wme, oy);
786 
787 	if (data->cx != ox) {
788 		data->lastcx = data->cx;
789 		data->lastsx = ox;
790 	}
791 	data->cx = data->lastcx;
792 
793 	n = 1;
794 	if (screen_size_y(s) > 2) {
795 		if (half_page)
796 			n = screen_size_y(s) / 2;
797 		else
798 			n = screen_size_y(s) - 2;
799 	}
800 
801 	if (data->oy < n) {
802 		data->oy = 0;
803 		if (data->cy + (n - data->oy) >= screen_size_y(data->backing))
804 			data->cy = screen_size_y(data->backing) - 1;
805 		else
806 			data->cy += n - data->oy;
807 	} else
808 		data->oy -= n;
809 
810 	if (data->screen.sel == NULL || !data->rectflag) {
811 		py = screen_hsize(data->backing) + data->cy - data->oy;
812 		px = window_copy_find_length(wme, py);
813 		if ((data->cx >= data->lastsx && data->cx != px) ||
814 		    data->cx > px)
815 			window_copy_cursor_end_of_line(wme);
816 	}
817 
818 	if (scroll_exit && data->oy == 0)
819 		return (1);
820 	if (data->searchmark != NULL && !data->timeout)
821 		window_copy_search_marks(wme, NULL, data->searchregex, 1);
822 	window_copy_update_selection(wme, 1, 0);
823 	window_copy_redraw_screen(wme);
824 	return (0);
825 }
826 
827 static void
window_copy_previous_paragraph(struct window_mode_entry * wme)828 window_copy_previous_paragraph(struct window_mode_entry *wme)
829 {
830 	struct window_copy_mode_data	*data = wme->data;
831 	u_int				 oy;
832 
833 	oy = screen_hsize(data->backing) + data->cy - data->oy;
834 
835 	while (oy > 0 && window_copy_find_length(wme, oy) == 0)
836 		oy--;
837 
838 	while (oy > 0 && window_copy_find_length(wme, oy) > 0)
839 		oy--;
840 
841 	window_copy_scroll_to(wme, 0, oy, 0);
842 }
843 
844 static void
window_copy_next_paragraph(struct window_mode_entry * wme)845 window_copy_next_paragraph(struct window_mode_entry *wme)
846 {
847 	struct window_copy_mode_data	*data = wme->data;
848 	struct screen			*s = &data->screen;
849 	u_int				 maxy, ox, oy;
850 
851 	oy = screen_hsize(data->backing) + data->cy - data->oy;
852 	maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
853 
854 	while (oy < maxy && window_copy_find_length(wme, oy) == 0)
855 		oy++;
856 
857 	while (oy < maxy && window_copy_find_length(wme, oy) > 0)
858 		oy++;
859 
860 	ox = window_copy_find_length(wme, oy);
861 	window_copy_scroll_to(wme, ox, oy, 0);
862 }
863 
864 char *
window_copy_get_word(struct window_pane * wp,u_int x,u_int y)865 window_copy_get_word(struct window_pane *wp, u_int x, u_int y)
866 {
867 	struct window_mode_entry	*wme = TAILQ_FIRST(&wp->modes);
868 	struct window_copy_mode_data	*data = wme->data;
869 	struct grid			*gd = data->screen.grid;
870 
871 	return (format_grid_word(gd, x, gd->hsize + y));
872 }
873 
874 char *
window_copy_get_line(struct window_pane * wp,u_int y)875 window_copy_get_line(struct window_pane *wp, u_int y)
876 {
877 	struct window_mode_entry	*wme = TAILQ_FIRST(&wp->modes);
878 	struct window_copy_mode_data	*data = wme->data;
879 	struct grid			*gd = data->screen.grid;
880 
881 	return (format_grid_line(gd, gd->hsize + y));
882 }
883 
884 static void *
window_copy_cursor_hyperlink_cb(struct format_tree * ft)885 window_copy_cursor_hyperlink_cb(struct format_tree *ft)
886 {
887 	struct window_pane		*wp = format_get_pane(ft);
888 	struct window_mode_entry	*wme = TAILQ_FIRST(&wp->modes);
889 	struct window_copy_mode_data	*data = wme->data;
890 	struct grid			*gd = data->screen.grid;
891 
892 	return (format_grid_hyperlink(gd, data->cx, gd->hsize + data->cy,
893 	    &data->screen));
894 }
895 
896 static void *
window_copy_cursor_word_cb(struct format_tree * ft)897 window_copy_cursor_word_cb(struct format_tree *ft)
898 {
899 	struct window_pane		*wp = format_get_pane(ft);
900 	struct window_mode_entry	*wme = TAILQ_FIRST(&wp->modes);
901 	struct window_copy_mode_data	*data = wme->data;
902 
903 	return (window_copy_get_word(wp, data->cx, data->cy));
904 }
905 
906 static void *
window_copy_cursor_line_cb(struct format_tree * ft)907 window_copy_cursor_line_cb(struct format_tree *ft)
908 {
909 	struct window_pane		*wp = format_get_pane(ft);
910 	struct window_mode_entry	*wme = TAILQ_FIRST(&wp->modes);
911 	struct window_copy_mode_data	*data = wme->data;
912 
913 	return (window_copy_get_line(wp, data->cy));
914 }
915 
916 static void *
window_copy_search_match_cb(struct format_tree * ft)917 window_copy_search_match_cb(struct format_tree *ft)
918 {
919 	struct window_pane		*wp = format_get_pane(ft);
920 	struct window_mode_entry	*wme = TAILQ_FIRST(&wp->modes);
921 	struct window_copy_mode_data	*data = wme->data;
922 
923 	return (window_copy_match_at_cursor(data));
924 }
925 
926 static void
window_copy_formats(struct window_mode_entry * wme,struct format_tree * ft)927 window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
928 {
929 	struct window_copy_mode_data	*data = wme->data;
930 	u_int				 hsize = screen_hsize(data->backing);
931 	struct grid_line		*gl;
932 
933 	gl = grid_get_line(data->backing->grid, hsize - data->oy);
934 	format_add(ft, "top_line_time", "%llu", (unsigned long long)gl->time);
935 
936 	format_add(ft, "scroll_position", "%d", data->oy);
937 	format_add(ft, "rectangle_toggle", "%d", data->rectflag);
938 
939 	format_add(ft, "copy_cursor_x", "%d", data->cx);
940 	format_add(ft, "copy_cursor_y", "%d", data->cy);
941 
942 	if (data->screen.sel != NULL) {
943 		format_add(ft, "selection_start_x", "%d", data->selx);
944 		format_add(ft, "selection_start_y", "%d", data->sely);
945 		format_add(ft, "selection_end_x", "%d", data->endselx);
946 		format_add(ft, "selection_end_y", "%d", data->endsely);
947 
948 		if (data->cursordrag != CURSORDRAG_NONE)
949 			format_add(ft, "selection_active", "1");
950 		else
951 			format_add(ft, "selection_active", "0");
952 		if (data->endselx != data->selx || data->endsely != data->sely)
953 			format_add(ft, "selection_present", "1");
954 		else
955 			format_add(ft, "selection_present", "0");
956 	} else {
957 		format_add(ft, "selection_active", "0");
958 		format_add(ft, "selection_present", "0");
959 	}
960 
961 	format_add(ft, "search_present", "%d", data->searchmark != NULL);
962 	format_add(ft, "search_timed_out", "%d", data->timeout);
963 	if (data->searchcount != -1) {
964 		format_add(ft, "search_count", "%d", data->searchcount);
965 		format_add(ft, "search_count_partial", "%d", data->searchmore);
966 	}
967 	format_add_cb(ft, "search_match", window_copy_search_match_cb);
968 
969 	format_add_cb(ft, "copy_cursor_word", window_copy_cursor_word_cb);
970 	format_add_cb(ft, "copy_cursor_line", window_copy_cursor_line_cb);
971 	format_add_cb(ft, "copy_cursor_hyperlink",
972 	    window_copy_cursor_hyperlink_cb);
973 }
974 
975 static void
window_copy_size_changed(struct window_mode_entry * wme)976 window_copy_size_changed(struct window_mode_entry *wme)
977 {
978 	struct window_copy_mode_data	*data = wme->data;
979 	struct screen			*s = &data->screen;
980 	struct screen_write_ctx		 ctx;
981 	int				 search = (data->searchmark != NULL);
982 
983 	window_copy_clear_selection(wme);
984 	window_copy_clear_marks(wme);
985 
986 	screen_write_start(&ctx, s);
987 	window_copy_write_lines(wme, &ctx, 0, screen_size_y(s));
988 	screen_write_stop(&ctx);
989 
990 	if (search && !data->timeout)
991 		window_copy_search_marks(wme, NULL, data->searchregex, 0);
992 	data->searchx = data->cx;
993 	data->searchy = data->cy;
994 	data->searcho = data->oy;
995 }
996 
997 static void
window_copy_resize(struct window_mode_entry * wme,u_int sx,u_int sy)998 window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
999 {
1000 	struct window_copy_mode_data	*data = wme->data;
1001 	struct screen			*s = &data->screen;
1002 	struct grid			*gd = data->backing->grid;
1003 	u_int				 cx, cy, wx, wy;
1004 	int				 reflow;
1005 
1006 	screen_resize(s, sx, sy, 0);
1007 	cx = data->cx;
1008 	cy = gd->hsize + data->cy - data->oy;
1009 	reflow = (gd->sx != sx);
1010 	if (reflow)
1011 		grid_wrap_position(gd, cx, cy, &wx, &wy);
1012 	screen_resize_cursor(data->backing, sx, sy, 1, 0, 0);
1013 	if (reflow)
1014 		grid_unwrap_position(gd, &cx, &cy, wx, wy);
1015 
1016 	data->cx = cx;
1017 	if (cy < gd->hsize) {
1018 		data->cy = 0;
1019 		data->oy = gd->hsize - cy;
1020 	} else {
1021 		data->cy = cy - gd->hsize;
1022 		data->oy = 0;
1023 	}
1024 
1025 	window_copy_size_changed(wme);
1026 	window_copy_redraw_screen(wme);
1027 }
1028 
1029 static const char *
window_copy_key_table(struct window_mode_entry * wme)1030 window_copy_key_table(struct window_mode_entry *wme)
1031 {
1032 	struct window_pane	*wp = wme->wp;
1033 
1034 	if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
1035 		return ("copy-mode-vi");
1036 	return ("copy-mode");
1037 }
1038 
1039 static int
window_copy_expand_search_string(struct window_copy_cmd_state * cs)1040 window_copy_expand_search_string(struct window_copy_cmd_state *cs)
1041 {
1042 	struct window_mode_entry	*wme = cs->wme;
1043 	struct window_copy_mode_data	*data = wme->data;
1044 	const char			*ss = args_string(cs->wargs, 0);
1045 	char				*expanded;
1046 
1047 	if (ss == NULL || *ss == '\0')
1048 		return (0);
1049 
1050 	if (args_has(cs->args, 'F')) {
1051 		expanded = format_single(NULL, ss, NULL, NULL, NULL, wme->wp);
1052 		if (*expanded == '\0') {
1053 			free(expanded);
1054 			return (0);
1055 		}
1056 		free(data->searchstr);
1057 		data->searchstr = expanded;
1058 	} else {
1059 		free(data->searchstr);
1060 		data->searchstr = xstrdup(ss);
1061 	}
1062 	return (1);
1063 }
1064 
1065 static enum window_copy_cmd_action
window_copy_cmd_append_selection(struct window_copy_cmd_state * cs)1066 window_copy_cmd_append_selection(struct window_copy_cmd_state *cs)
1067 {
1068 	struct window_mode_entry	*wme = cs->wme;
1069 	struct session			*s = cs->s;
1070 
1071 	if (s != NULL)
1072 		window_copy_append_selection(wme);
1073 	window_copy_clear_selection(wme);
1074 	return (WINDOW_COPY_CMD_REDRAW);
1075 }
1076 
1077 static enum window_copy_cmd_action
window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state * cs)1078 window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs)
1079 {
1080 	struct window_mode_entry	*wme = cs->wme;
1081 	struct session			*s = cs->s;
1082 
1083 	if (s != NULL)
1084 		window_copy_append_selection(wme);
1085 	window_copy_clear_selection(wme);
1086 	return (WINDOW_COPY_CMD_CANCEL);
1087 }
1088 
1089 static enum window_copy_cmd_action
window_copy_cmd_back_to_indentation(struct window_copy_cmd_state * cs)1090 window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs)
1091 {
1092 	struct window_mode_entry	*wme = cs->wme;
1093 
1094 	window_copy_cursor_back_to_indentation(wme);
1095 	return (WINDOW_COPY_CMD_NOTHING);
1096 }
1097 
1098 static enum window_copy_cmd_action
window_copy_cmd_begin_selection(struct window_copy_cmd_state * cs)1099 window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs)
1100 {
1101 	struct window_mode_entry	*wme = cs->wme;
1102 	struct client			*c = cs->c;
1103 	struct mouse_event		*m = cs->m;
1104 	struct window_copy_mode_data	*data = wme->data;
1105 
1106 	if (m != NULL) {
1107 		window_copy_start_drag(c, m);
1108 		return (WINDOW_COPY_CMD_NOTHING);
1109 	}
1110 
1111 	data->lineflag = LINE_SEL_NONE;
1112 	data->selflag = SEL_CHAR;
1113 	window_copy_start_selection(wme);
1114 	return (WINDOW_COPY_CMD_REDRAW);
1115 }
1116 
1117 static enum window_copy_cmd_action
window_copy_cmd_stop_selection(struct window_copy_cmd_state * cs)1118 window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs)
1119 {
1120 	struct window_mode_entry	*wme = cs->wme;
1121 	struct window_copy_mode_data	*data = wme->data;
1122 
1123 	data->cursordrag = CURSORDRAG_NONE;
1124 	data->lineflag = LINE_SEL_NONE;
1125 	data->selflag = SEL_CHAR;
1126 	return (WINDOW_COPY_CMD_NOTHING);
1127 }
1128 
1129 static enum window_copy_cmd_action
window_copy_cmd_bottom_line(struct window_copy_cmd_state * cs)1130 window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs)
1131 {
1132 	struct window_mode_entry	*wme = cs->wme;
1133 	struct window_copy_mode_data	*data = wme->data;
1134 
1135 	data->cx = 0;
1136 	data->cy = screen_size_y(&data->screen) - 1;
1137 
1138 	window_copy_update_selection(wme, 1, 0);
1139 	return (WINDOW_COPY_CMD_REDRAW);
1140 }
1141 
1142 static enum window_copy_cmd_action
window_copy_cmd_cancel(__unused struct window_copy_cmd_state * cs)1143 window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs)
1144 {
1145 	return (WINDOW_COPY_CMD_CANCEL);
1146 }
1147 
1148 static enum window_copy_cmd_action
window_copy_cmd_clear_selection(struct window_copy_cmd_state * cs)1149 window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs)
1150 {
1151 	struct window_mode_entry	*wme = cs->wme;
1152 
1153 	window_copy_clear_selection(wme);
1154 	return (WINDOW_COPY_CMD_REDRAW);
1155 }
1156 
1157 static enum window_copy_cmd_action
window_copy_do_copy_end_of_line(struct window_copy_cmd_state * cs,int pipe,int cancel)1158 window_copy_do_copy_end_of_line(struct window_copy_cmd_state *cs, int pipe,
1159     int cancel)
1160 {
1161 	struct window_mode_entry	 *wme = cs->wme;
1162 	struct client			 *c = cs->c;
1163 	struct session			 *s = cs->s;
1164 	struct winlink			 *wl = cs->wl;
1165 	struct window_pane		 *wp = wme->wp;
1166 	u_int				  count = args_count(cs->wargs);
1167 	u_int				  np = wme->prefix, ocx, ocy, ooy;
1168 	struct window_copy_mode_data	 *data = wme->data;
1169 	char				 *prefix = NULL, *command = NULL;
1170 	const char			 *arg0 = args_string(cs->wargs, 0);
1171 	const char			 *arg1 = args_string(cs->wargs, 1);
1172 	int				  set_paste = !args_has(cs->wargs, 'P');
1173 	int				  set_clip = !args_has(cs->wargs, 'C');
1174 
1175 	if (pipe) {
1176 		if (count == 2)
1177 			prefix = format_single(NULL, arg1, c, s, wl, wp);
1178 		if (s != NULL && count > 0 && *arg0 != '\0')
1179 			command = format_single(NULL, arg0, c, s, wl, wp);
1180 	} else {
1181 		if (count == 1)
1182 			prefix = format_single(NULL, arg0, c, s, wl, wp);
1183 	}
1184 
1185 	ocx = data->cx;
1186 	ocy = data->cy;
1187 	ooy = data->oy;
1188 
1189 	window_copy_start_selection(wme);
1190 	for (; np > 1; np--)
1191 		window_copy_cursor_down(wme, 0);
1192 	window_copy_cursor_end_of_line(wme);
1193 
1194 	if (s != NULL) {
1195 		if (pipe)
1196 			window_copy_copy_pipe(wme, s, prefix, command,
1197 			    set_paste, set_clip);
1198 		else
1199 			window_copy_copy_selection(wme, prefix,
1200 			    set_paste, set_clip);
1201 
1202 		if (cancel) {
1203 			free(prefix);
1204 			free(command);
1205 			return (WINDOW_COPY_CMD_CANCEL);
1206 		}
1207 	}
1208 	window_copy_clear_selection(wme);
1209 
1210 	data->cx = ocx;
1211 	data->cy = ocy;
1212 	data->oy = ooy;
1213 
1214 	free(prefix);
1215 	free(command);
1216 	return (WINDOW_COPY_CMD_REDRAW);
1217 }
1218 
1219 static enum window_copy_cmd_action
window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state * cs)1220 window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs)
1221 {
1222 	return (window_copy_do_copy_end_of_line(cs, 0, 0));
1223 }
1224 
1225 static enum window_copy_cmd_action
window_copy_cmd_copy_end_of_line_and_cancel(struct window_copy_cmd_state * cs)1226 window_copy_cmd_copy_end_of_line_and_cancel(struct window_copy_cmd_state *cs)
1227 {
1228 	return (window_copy_do_copy_end_of_line(cs, 0, 1));
1229 }
1230 
1231 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_end_of_line(struct window_copy_cmd_state * cs)1232 window_copy_cmd_copy_pipe_end_of_line(struct window_copy_cmd_state *cs)
1233 {
1234 	return (window_copy_do_copy_end_of_line(cs, 1, 0));
1235 }
1236 
1237 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_end_of_line_and_cancel(struct window_copy_cmd_state * cs)1238 window_copy_cmd_copy_pipe_end_of_line_and_cancel(
1239     struct window_copy_cmd_state *cs)
1240 {
1241 	return (window_copy_do_copy_end_of_line(cs, 1, 1));
1242 }
1243 
1244 static enum window_copy_cmd_action
window_copy_do_copy_line(struct window_copy_cmd_state * cs,int pipe,int cancel)1245 window_copy_do_copy_line(struct window_copy_cmd_state *cs, int pipe, int cancel)
1246 {
1247 	struct window_mode_entry	 *wme = cs->wme;
1248 	struct client			 *c = cs->c;
1249 	struct session			 *s = cs->s;
1250 	struct winlink			 *wl = cs->wl;
1251 	struct window_pane		 *wp = wme->wp;
1252 	struct window_copy_mode_data	 *data = wme->data;
1253 	u_int				  count = args_count(cs->wargs);
1254 	u_int				  np = wme->prefix, ocx, ocy, ooy;
1255 	char				 *prefix = NULL, *command = NULL;
1256 	const char			 *arg0 = args_string(cs->wargs, 0);
1257 	const char			 *arg1 = args_string(cs->wargs, 1);
1258 	int				  set_paste = !args_has(cs->wargs, 'P');
1259 	int				  set_clip = !args_has(cs->wargs, 'C');
1260 
1261 	if (pipe) {
1262 		if (count == 2)
1263 			prefix = format_single(NULL, arg1, c, s, wl, wp);
1264 		if (s != NULL && count > 0 && *arg0 != '\0')
1265 			command = format_single(NULL, arg0, c, s, wl, wp);
1266 	} else {
1267 		if (count == 1)
1268 			prefix = format_single(NULL, arg0, c, s, wl, wp);
1269 	}
1270 
1271 	ocx = data->cx;
1272 	ocy = data->cy;
1273 	ooy = data->oy;
1274 
1275 	data->selflag = SEL_CHAR;
1276 	window_copy_cursor_start_of_line(wme);
1277 	window_copy_start_selection(wme);
1278 	for (; np > 1; np--)
1279 		window_copy_cursor_down(wme, 0);
1280 	window_copy_cursor_end_of_line(wme);
1281 
1282 	if (s != NULL) {
1283 		if (pipe)
1284 			window_copy_copy_pipe(wme, s, prefix, command,
1285 			    set_paste, set_clip);
1286 		else
1287 			window_copy_copy_selection(wme, prefix,
1288 			    set_paste, set_clip);
1289 
1290 		if (cancel) {
1291 			free(prefix);
1292 			free(command);
1293 			return (WINDOW_COPY_CMD_CANCEL);
1294 		}
1295 	}
1296 	window_copy_clear_selection(wme);
1297 
1298 	data->cx = ocx;
1299 	data->cy = ocy;
1300 	data->oy = ooy;
1301 
1302 	free(prefix);
1303 	free(command);
1304 	return (WINDOW_COPY_CMD_REDRAW);
1305 }
1306 
1307 static enum window_copy_cmd_action
window_copy_cmd_copy_line(struct window_copy_cmd_state * cs)1308 window_copy_cmd_copy_line(struct window_copy_cmd_state *cs)
1309 {
1310 	return (window_copy_do_copy_line(cs, 0, 0));
1311 }
1312 
1313 static enum window_copy_cmd_action
window_copy_cmd_copy_line_and_cancel(struct window_copy_cmd_state * cs)1314 window_copy_cmd_copy_line_and_cancel(struct window_copy_cmd_state *cs)
1315 {
1316 	return (window_copy_do_copy_line(cs, 0, 1));
1317 }
1318 
1319 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_line(struct window_copy_cmd_state * cs)1320 window_copy_cmd_copy_pipe_line(struct window_copy_cmd_state *cs)
1321 {
1322 	return (window_copy_do_copy_line(cs, 1, 0));
1323 }
1324 
1325 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_line_and_cancel(struct window_copy_cmd_state * cs)1326 window_copy_cmd_copy_pipe_line_and_cancel(struct window_copy_cmd_state *cs)
1327 {
1328 	return (window_copy_do_copy_line(cs, 1, 1));
1329 }
1330 
1331 static enum window_copy_cmd_action
window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state * cs)1332 window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs)
1333 {
1334 	struct window_mode_entry	*wme = cs->wme;
1335 	struct client			*c = cs->c;
1336 	struct session			*s = cs->s;
1337 	struct winlink			*wl = cs->wl;
1338 	struct window_pane		*wp = wme->wp;
1339 	char				*prefix = NULL;
1340 	const char			*arg0 = args_string(cs->wargs, 0);
1341 	int				 set_paste = !args_has(cs->wargs, 'P');
1342 	int				 set_clip = !args_has(cs->wargs, 'C');
1343 
1344 	if (arg0 != NULL)
1345 		prefix = format_single(NULL, arg0, c, s, wl, wp);
1346 
1347 	if (s != NULL)
1348 		window_copy_copy_selection(wme, prefix, set_paste, set_clip);
1349 
1350 	free(prefix);
1351 	return (WINDOW_COPY_CMD_NOTHING);
1352 }
1353 
1354 static enum window_copy_cmd_action
window_copy_cmd_copy_selection(struct window_copy_cmd_state * cs)1355 window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs)
1356 {
1357 	struct window_mode_entry	*wme = cs->wme;
1358 
1359 	window_copy_cmd_copy_selection_no_clear(cs);
1360 	window_copy_clear_selection(wme);
1361 	return (WINDOW_COPY_CMD_REDRAW);
1362 }
1363 
1364 static enum window_copy_cmd_action
window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state * cs)1365 window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
1366 {
1367 	struct window_mode_entry	*wme = cs->wme;
1368 
1369 	window_copy_cmd_copy_selection_no_clear(cs);
1370 	window_copy_clear_selection(wme);
1371 	return (WINDOW_COPY_CMD_CANCEL);
1372 }
1373 
1374 static enum window_copy_cmd_action
window_copy_cmd_cursor_down(struct window_copy_cmd_state * cs)1375 window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
1376 {
1377 	struct window_mode_entry	*wme = cs->wme;
1378 	u_int				 np = wme->prefix;
1379 
1380 	for (; np != 0; np--)
1381 		window_copy_cursor_down(wme, 0);
1382 	return (WINDOW_COPY_CMD_NOTHING);
1383 }
1384 
1385 static enum window_copy_cmd_action
window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state * cs)1386 window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs)
1387 {
1388 	struct window_mode_entry	*wme = cs->wme;
1389 	struct window_copy_mode_data	*data = wme->data;
1390 	u_int				 np = wme->prefix, cy;
1391 
1392 	cy = data->cy;
1393 	for (; np != 0; np--)
1394 		window_copy_cursor_down(wme, 0);
1395 	if (cy == data->cy && data->oy == 0)
1396 		return (WINDOW_COPY_CMD_CANCEL);
1397 	return (WINDOW_COPY_CMD_NOTHING);
1398 }
1399 
1400 static enum window_copy_cmd_action
window_copy_cmd_cursor_left(struct window_copy_cmd_state * cs)1401 window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs)
1402 {
1403 	struct window_mode_entry	*wme = cs->wme;
1404 	u_int				 np = wme->prefix;
1405 
1406 	for (; np != 0; np--)
1407 		window_copy_cursor_left(wme);
1408 	return (WINDOW_COPY_CMD_NOTHING);
1409 }
1410 
1411 static enum window_copy_cmd_action
window_copy_cmd_cursor_right(struct window_copy_cmd_state * cs)1412 window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
1413 {
1414 	struct window_mode_entry	*wme = cs->wme;
1415 	struct window_copy_mode_data	*data = wme->data;
1416 	u_int				 np = wme->prefix;
1417 
1418 	for (; np != 0; np--) {
1419 		window_copy_cursor_right(wme, data->screen.sel != NULL &&
1420 		    data->rectflag);
1421 	}
1422 	return (WINDOW_COPY_CMD_NOTHING);
1423 }
1424 
1425 /* Scroll line containing the cursor to the given position. */
1426 static enum window_copy_cmd_action
window_copy_cmd_scroll_to(struct window_copy_cmd_state * cs,u_int to)1427 window_copy_cmd_scroll_to(struct window_copy_cmd_state *cs, u_int to)
1428 {
1429 	struct window_mode_entry	*wme = cs->wme;
1430 	struct window_copy_mode_data	*data = wme->data;
1431 	u_int				 oy, delta;
1432 	int				 scroll_up; /* >0 up, <0 down */
1433 
1434 	scroll_up = data->cy - to;
1435 	delta = abs(scroll_up);
1436 	oy = screen_hsize(data->backing) - data->oy;
1437 
1438 	/*
1439 	 * oy is the maximum scroll down amount, while data->oy is the maximum
1440 	 * scroll up amount.
1441 	 */
1442 	if (scroll_up > 0 && data->oy >= delta) {
1443 		window_copy_scroll_up(wme, delta);
1444 		data->cy -= delta;
1445 	} else if (scroll_up < 0 && oy >= delta) {
1446 		window_copy_scroll_down(wme, delta);
1447 		data->cy += delta;
1448 	}
1449 
1450 	window_copy_update_selection(wme, 0, 0);
1451 	return (WINDOW_COPY_CMD_REDRAW);
1452 }
1453 
1454 /* Scroll line containing the cursor to the bottom. */
1455 static enum window_copy_cmd_action
window_copy_cmd_scroll_bottom(struct window_copy_cmd_state * cs)1456 window_copy_cmd_scroll_bottom(struct window_copy_cmd_state *cs)
1457 {
1458 	struct window_copy_mode_data	*data = cs->wme->data;
1459 	u_int				 bottom;
1460 
1461 	bottom = screen_size_y(&data->screen) - 1;
1462 	return (window_copy_cmd_scroll_to(cs, bottom));
1463 }
1464 
1465 /* Scroll line containing the cursor to the middle. */
1466 static enum window_copy_cmd_action
window_copy_cmd_scroll_middle(struct window_copy_cmd_state * cs)1467 window_copy_cmd_scroll_middle(struct window_copy_cmd_state *cs)
1468 {
1469 	struct window_copy_mode_data	*data = cs->wme->data;
1470 	u_int				 mid_value;
1471 
1472 	mid_value = (screen_size_y(&data->screen) - 1) / 2;
1473 	return (window_copy_cmd_scroll_to(cs, mid_value));
1474 }
1475 
1476 /* Scroll line containing the cursor to the top. */
1477 static enum window_copy_cmd_action
window_copy_cmd_scroll_top(struct window_copy_cmd_state * cs)1478 window_copy_cmd_scroll_top(struct window_copy_cmd_state *cs)
1479 {
1480 	return (window_copy_cmd_scroll_to(cs, 0));
1481 }
1482 
1483 static enum window_copy_cmd_action
window_copy_cmd_cursor_up(struct window_copy_cmd_state * cs)1484 window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
1485 {
1486 	struct window_mode_entry	*wme = cs->wme;
1487 	u_int				 np = wme->prefix;
1488 
1489 	for (; np != 0; np--)
1490 		window_copy_cursor_up(wme, 0);
1491 	return (WINDOW_COPY_CMD_NOTHING);
1492 }
1493 
1494 static enum window_copy_cmd_action
window_copy_cmd_end_of_line(struct window_copy_cmd_state * cs)1495 window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs)
1496 {
1497 	struct window_mode_entry	*wme = cs->wme;
1498 
1499 	window_copy_cursor_end_of_line(wme);
1500 	return (WINDOW_COPY_CMD_NOTHING);
1501 }
1502 
1503 static enum window_copy_cmd_action
window_copy_cmd_halfpage_down(struct window_copy_cmd_state * cs)1504 window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs)
1505 {
1506 	struct window_mode_entry	*wme = cs->wme;
1507 	struct window_copy_mode_data	*data = wme->data;
1508 	u_int				 np = wme->prefix;
1509 
1510 	for (; np != 0; np--) {
1511 		if (window_copy_pagedown1(wme, 1, data->scroll_exit))
1512 			return (WINDOW_COPY_CMD_CANCEL);
1513 	}
1514 	return (WINDOW_COPY_CMD_NOTHING);
1515 }
1516 
1517 static enum window_copy_cmd_action
window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state * cs)1518 window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs)
1519 {
1520 
1521 	struct window_mode_entry	*wme = cs->wme;
1522 	u_int				 np = wme->prefix;
1523 
1524 	for (; np != 0; np--) {
1525 		if (window_copy_pagedown1(wme, 1, 1))
1526 			return (WINDOW_COPY_CMD_CANCEL);
1527 	}
1528 	return (WINDOW_COPY_CMD_NOTHING);
1529 }
1530 
1531 static enum window_copy_cmd_action
window_copy_cmd_halfpage_up(struct window_copy_cmd_state * cs)1532 window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs)
1533 {
1534 	struct window_mode_entry	*wme = cs->wme;
1535 	u_int				 np = wme->prefix;
1536 
1537 	for (; np != 0; np--)
1538 		window_copy_pageup1(wme, 1);
1539 	return (WINDOW_COPY_CMD_NOTHING);
1540 }
1541 
1542 static enum window_copy_cmd_action
window_copy_cmd_toggle_position(struct window_copy_cmd_state * cs)1543 window_copy_cmd_toggle_position(struct window_copy_cmd_state *cs)
1544 {
1545 	struct window_mode_entry	*wme = cs->wme;
1546 	struct window_copy_mode_data	*data = wme->data;
1547 
1548 	data->hide_position = !data->hide_position;
1549 	return (WINDOW_COPY_CMD_REDRAW);
1550 }
1551 
1552 static enum window_copy_cmd_action
window_copy_cmd_history_bottom(struct window_copy_cmd_state * cs)1553 window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
1554 {
1555 	struct window_mode_entry	*wme = cs->wme;
1556 	struct window_copy_mode_data	*data = wme->data;
1557 	struct screen			*s = data->backing;
1558 	u_int				 oy;
1559 
1560 	oy = screen_hsize(s) + data->cy - data->oy;
1561 	if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
1562 		window_copy_other_end(wme);
1563 
1564 	data->cy = screen_size_y(&data->screen) - 1;
1565 	data->cx = window_copy_find_length(wme, screen_hsize(s) + data->cy);
1566 	data->oy = 0;
1567 
1568 	if (data->searchmark != NULL && !data->timeout)
1569 		window_copy_search_marks(wme, NULL, data->searchregex, 1);
1570 	window_copy_update_selection(wme, 1, 0);
1571 	return (WINDOW_COPY_CMD_REDRAW);
1572 }
1573 
1574 static enum window_copy_cmd_action
window_copy_cmd_history_top(struct window_copy_cmd_state * cs)1575 window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
1576 {
1577 	struct window_mode_entry	*wme = cs->wme;
1578 	struct window_copy_mode_data	*data = wme->data;
1579 	u_int				 oy;
1580 
1581 	oy = screen_hsize(data->backing) + data->cy - data->oy;
1582 	if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
1583 		window_copy_other_end(wme);
1584 
1585 	data->cy = 0;
1586 	data->cx = 0;
1587 	data->oy = screen_hsize(data->backing);
1588 
1589 	if (data->searchmark != NULL && !data->timeout)
1590 		window_copy_search_marks(wme, NULL, data->searchregex, 1);
1591 	window_copy_update_selection(wme, 1, 0);
1592 	return (WINDOW_COPY_CMD_REDRAW);
1593 }
1594 
1595 static enum window_copy_cmd_action
window_copy_cmd_jump_again(struct window_copy_cmd_state * cs)1596 window_copy_cmd_jump_again(struct window_copy_cmd_state *cs)
1597 {
1598 	struct window_mode_entry	*wme = cs->wme;
1599 	struct window_copy_mode_data	*data = wme->data;
1600 	u_int				 np = wme->prefix;
1601 
1602 	switch (data->jumptype) {
1603 	case WINDOW_COPY_JUMPFORWARD:
1604 		for (; np != 0; np--)
1605 			window_copy_cursor_jump(wme);
1606 		break;
1607 	case WINDOW_COPY_JUMPBACKWARD:
1608 		for (; np != 0; np--)
1609 			window_copy_cursor_jump_back(wme);
1610 		break;
1611 	case WINDOW_COPY_JUMPTOFORWARD:
1612 		for (; np != 0; np--)
1613 			window_copy_cursor_jump_to(wme);
1614 		break;
1615 	case WINDOW_COPY_JUMPTOBACKWARD:
1616 		for (; np != 0; np--)
1617 			window_copy_cursor_jump_to_back(wme);
1618 		break;
1619 	}
1620 	return (WINDOW_COPY_CMD_NOTHING);
1621 }
1622 
1623 static enum window_copy_cmd_action
window_copy_cmd_jump_reverse(struct window_copy_cmd_state * cs)1624 window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs)
1625 {
1626 	struct window_mode_entry	*wme = cs->wme;
1627 	struct window_copy_mode_data	*data = wme->data;
1628 	u_int				 np = wme->prefix;
1629 
1630 	switch (data->jumptype) {
1631 	case WINDOW_COPY_JUMPFORWARD:
1632 		for (; np != 0; np--)
1633 			window_copy_cursor_jump_back(wme);
1634 		break;
1635 	case WINDOW_COPY_JUMPBACKWARD:
1636 		for (; np != 0; np--)
1637 			window_copy_cursor_jump(wme);
1638 		break;
1639 	case WINDOW_COPY_JUMPTOFORWARD:
1640 		for (; np != 0; np--)
1641 			window_copy_cursor_jump_to_back(wme);
1642 		break;
1643 	case WINDOW_COPY_JUMPTOBACKWARD:
1644 		for (; np != 0; np--)
1645 			window_copy_cursor_jump_to(wme);
1646 		break;
1647 	}
1648 	return (WINDOW_COPY_CMD_NOTHING);
1649 }
1650 
1651 static enum window_copy_cmd_action
window_copy_cmd_middle_line(struct window_copy_cmd_state * cs)1652 window_copy_cmd_middle_line(struct window_copy_cmd_state *cs)
1653 {
1654 	struct window_mode_entry	*wme = cs->wme;
1655 	struct window_copy_mode_data	*data = wme->data;
1656 
1657 	data->cx = 0;
1658 	data->cy = (screen_size_y(&data->screen) - 1) / 2;
1659 
1660 	window_copy_update_selection(wme, 1, 0);
1661 	return (WINDOW_COPY_CMD_REDRAW);
1662 }
1663 
1664 static enum window_copy_cmd_action
window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state * cs)1665 window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs)
1666 {
1667 	struct window_mode_entry	*wme = cs->wme;
1668 	u_int				 np = wme->prefix;
1669 	struct window_copy_mode_data	*data = wme->data;
1670 	struct screen			*s = data->backing;
1671 	char				 open[] = "{[(", close[] = "}])";
1672 	char				 tried, found, start, *cp;
1673 	u_int				 px, py, xx, n;
1674 	struct grid_cell		 gc;
1675 	int				 failed;
1676 
1677 	for (; np != 0; np--) {
1678 		/* Get cursor position and line length. */
1679 		px = data->cx;
1680 		py = screen_hsize(s) + data->cy - data->oy;
1681 		xx = window_copy_find_length(wme, py);
1682 		if (xx == 0)
1683 			break;
1684 
1685 		/*
1686 		 * Get the current character. If not on a bracket, try the
1687 		 * previous. If still not, then behave like previous-word.
1688 		 */
1689 		tried = 0;
1690 	retry:
1691 		grid_get_cell(s->grid, px, py, &gc);
1692 		if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1693 			cp = NULL;
1694 		else {
1695 			found = *gc.data.data;
1696 			cp = strchr(close, found);
1697 		}
1698 		if (cp == NULL) {
1699 			if (data->modekeys == MODEKEY_EMACS) {
1700 				if (!tried && px > 0) {
1701 					px--;
1702 					tried = 1;
1703 					goto retry;
1704 				}
1705 				window_copy_cursor_previous_word(wme, close, 1);
1706 			}
1707 			continue;
1708 		}
1709 		start = open[cp - close];
1710 
1711 		/* Walk backward until the matching bracket is reached. */
1712 		n = 1;
1713 		failed = 0;
1714 		do {
1715 			if (px == 0) {
1716 				if (py == 0) {
1717 					failed = 1;
1718 					break;
1719 				}
1720 				do {
1721 					py--;
1722 					xx = window_copy_find_length(wme, py);
1723 				} while (xx == 0 && py > 0);
1724 				if (xx == 0 && py == 0) {
1725 					failed = 1;
1726 					break;
1727 				}
1728 				px = xx - 1;
1729 			} else
1730 				px--;
1731 
1732 			grid_get_cell(s->grid, px, py, &gc);
1733 			if (gc.data.size == 1 &&
1734 			    (~gc.flags & GRID_FLAG_PADDING)) {
1735 				if (*gc.data.data == found)
1736 					n++;
1737 				else if (*gc.data.data == start)
1738 					n--;
1739 			}
1740 		} while (n != 0);
1741 
1742 		/* Move the cursor to the found location if any. */
1743 		if (!failed)
1744 			window_copy_scroll_to(wme, px, py, 0);
1745 	}
1746 
1747 	return (WINDOW_COPY_CMD_NOTHING);
1748 }
1749 
1750 static enum window_copy_cmd_action
window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state * cs)1751 window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs)
1752 {
1753 	struct window_mode_entry	*wme = cs->wme;
1754 	u_int				 np = wme->prefix;
1755 	struct window_copy_mode_data	*data = wme->data;
1756 	struct screen			*s = data->backing;
1757 	char				 open[] = "{[(", close[] = "}])";
1758 	char				 tried, found, end, *cp;
1759 	u_int				 px, py, xx, yy, sx, sy, n;
1760 	struct grid_cell		 gc;
1761 	int				 failed;
1762 	struct grid_line		*gl;
1763 
1764 	for (; np != 0; np--) {
1765 		/* Get cursor position and line length. */
1766 		px = data->cx;
1767 		py = screen_hsize(s) + data->cy - data->oy;
1768 		xx = window_copy_find_length(wme, py);
1769 		yy = screen_hsize(s) + screen_size_y(s) - 1;
1770 		if (xx == 0)
1771 			break;
1772 
1773 		/*
1774 		 * Get the current character. If not on a bracket, try the
1775 		 * next. If still not, then behave like next-word.
1776 		 */
1777 		tried = 0;
1778 	retry:
1779 		grid_get_cell(s->grid, px, py, &gc);
1780 		if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1781 			cp = NULL;
1782 		else {
1783 			found = *gc.data.data;
1784 
1785 			/*
1786 			 * In vi mode, attempt to move to previous bracket if a
1787 			 * closing bracket is found first. If this fails,
1788 			 * return to the original cursor position.
1789 			 */
1790 			cp = strchr(close, found);
1791 			if (cp != NULL && data->modekeys == MODEKEY_VI) {
1792 				sx = data->cx;
1793 				sy = screen_hsize(s) + data->cy - data->oy;
1794 
1795 				window_copy_scroll_to(wme, px, py, 0);
1796 				window_copy_cmd_previous_matching_bracket(cs);
1797 
1798 				px = data->cx;
1799 				py = screen_hsize(s) + data->cy - data->oy;
1800 				grid_get_cell(s->grid, px, py, &gc);
1801 				if (gc.data.size == 1 &&
1802 				    (~gc.flags & GRID_FLAG_PADDING) &&
1803 				    strchr(close, *gc.data.data) != NULL)
1804 					window_copy_scroll_to(wme, sx, sy, 0);
1805 				break;
1806 			}
1807 
1808 			cp = strchr(open, found);
1809 		}
1810 		if (cp == NULL) {
1811 			if (data->modekeys == MODEKEY_EMACS) {
1812 				if (!tried && px <= xx) {
1813 					px++;
1814 					tried = 1;
1815 					goto retry;
1816 				}
1817 				window_copy_cursor_next_word_end(wme, open, 0);
1818 				continue;
1819 			}
1820 			/* For vi, continue searching for bracket until EOL. */
1821 			if (px > xx) {
1822 				if (py == yy)
1823 					continue;
1824 				gl = grid_get_line(s->grid, py);
1825 				if (~gl->flags & GRID_LINE_WRAPPED)
1826 					continue;
1827 				if (gl->cellsize > s->grid->sx)
1828 					continue;
1829 				px = 0;
1830 				py++;
1831 				xx = window_copy_find_length(wme, py);
1832 			} else
1833 				px++;
1834 			goto retry;
1835 		}
1836 		end = close[cp - open];
1837 
1838 		/* Walk forward until the matching bracket is reached. */
1839 		n = 1;
1840 		failed = 0;
1841 		do {
1842 			if (px > xx) {
1843 				if (py == yy) {
1844 					failed = 1;
1845 					break;
1846 				}
1847 				px = 0;
1848 				py++;
1849 				xx = window_copy_find_length(wme, py);
1850 			} else
1851 				px++;
1852 
1853 			grid_get_cell(s->grid, px, py, &gc);
1854 			if (gc.data.size == 1 &&
1855 			    (~gc.flags & GRID_FLAG_PADDING)) {
1856 				if (*gc.data.data == found)
1857 					n++;
1858 				else if (*gc.data.data == end)
1859 					n--;
1860 			}
1861 		} while (n != 0);
1862 
1863 		/* Move the cursor to the found location if any. */
1864 		if (!failed)
1865 			window_copy_scroll_to(wme, px, py, 0);
1866 	}
1867 
1868 	return (WINDOW_COPY_CMD_NOTHING);
1869 }
1870 
1871 static enum window_copy_cmd_action
window_copy_cmd_next_paragraph(struct window_copy_cmd_state * cs)1872 window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs)
1873 {
1874 	struct window_mode_entry	*wme = cs->wme;
1875 	u_int				 np = wme->prefix;
1876 
1877 	for (; np != 0; np--)
1878 		window_copy_next_paragraph(wme);
1879 	return (WINDOW_COPY_CMD_NOTHING);
1880 }
1881 
1882 static enum window_copy_cmd_action
window_copy_cmd_next_space(struct window_copy_cmd_state * cs)1883 window_copy_cmd_next_space(struct window_copy_cmd_state *cs)
1884 {
1885 	struct window_mode_entry	*wme = cs->wme;
1886 	u_int				 np = wme->prefix;
1887 
1888 	for (; np != 0; np--)
1889 		window_copy_cursor_next_word(wme, "");
1890 	return (WINDOW_COPY_CMD_NOTHING);
1891 }
1892 
1893 static enum window_copy_cmd_action
window_copy_cmd_next_space_end(struct window_copy_cmd_state * cs)1894 window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs)
1895 {
1896 	struct window_mode_entry	*wme = cs->wme;
1897 	u_int				 np = wme->prefix;
1898 
1899 	for (; np != 0; np--)
1900 		window_copy_cursor_next_word_end(wme, "", 0);
1901 	return (WINDOW_COPY_CMD_NOTHING);
1902 }
1903 
1904 static enum window_copy_cmd_action
window_copy_cmd_next_word(struct window_copy_cmd_state * cs)1905 window_copy_cmd_next_word(struct window_copy_cmd_state *cs)
1906 {
1907 	struct window_mode_entry	*wme = cs->wme;
1908 	u_int				 np = wme->prefix;
1909 	const char			*separators;
1910 
1911 	separators = options_get_string(cs->s->options, "word-separators");
1912 
1913 	for (; np != 0; np--)
1914 		window_copy_cursor_next_word(wme, separators);
1915 	return (WINDOW_COPY_CMD_NOTHING);
1916 }
1917 
1918 static enum window_copy_cmd_action
window_copy_cmd_next_word_end(struct window_copy_cmd_state * cs)1919 window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs)
1920 {
1921 	struct window_mode_entry	*wme = cs->wme;
1922 	u_int				 np = wme->prefix;
1923 	const char			*separators;
1924 
1925 	separators = options_get_string(cs->s->options, "word-separators");
1926 
1927 	for (; np != 0; np--)
1928 		window_copy_cursor_next_word_end(wme, separators, 0);
1929 	return (WINDOW_COPY_CMD_NOTHING);
1930 }
1931 
1932 static enum window_copy_cmd_action
window_copy_cmd_other_end(struct window_copy_cmd_state * cs)1933 window_copy_cmd_other_end(struct window_copy_cmd_state *cs)
1934 {
1935 	struct window_mode_entry	*wme = cs->wme;
1936 	u_int				 np = wme->prefix;
1937 	struct window_copy_mode_data	*data = wme->data;
1938 
1939 	data->selflag = SEL_CHAR;
1940 	if ((np % 2) != 0)
1941 		window_copy_other_end(wme);
1942 	return (WINDOW_COPY_CMD_NOTHING);
1943 }
1944 
1945 static enum window_copy_cmd_action
window_copy_cmd_page_down(struct window_copy_cmd_state * cs)1946 window_copy_cmd_page_down(struct window_copy_cmd_state *cs)
1947 {
1948 	struct window_mode_entry	*wme = cs->wme;
1949 	struct window_copy_mode_data	*data = wme->data;
1950 	u_int				 np = wme->prefix;
1951 
1952 	for (; np != 0; np--) {
1953 		if (window_copy_pagedown1(wme, 0, data->scroll_exit))
1954 			return (WINDOW_COPY_CMD_CANCEL);
1955 	}
1956 	return (WINDOW_COPY_CMD_NOTHING);
1957 }
1958 
1959 static enum window_copy_cmd_action
window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state * cs)1960 window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs)
1961 {
1962 	struct window_mode_entry	*wme = cs->wme;
1963 	u_int				 np = wme->prefix;
1964 
1965 	for (; np != 0; np--) {
1966 		if (window_copy_pagedown1(wme, 0, 1))
1967 			return (WINDOW_COPY_CMD_CANCEL);
1968 	}
1969 	return (WINDOW_COPY_CMD_NOTHING);
1970 }
1971 
1972 static enum window_copy_cmd_action
window_copy_cmd_page_up(struct window_copy_cmd_state * cs)1973 window_copy_cmd_page_up(struct window_copy_cmd_state *cs)
1974 {
1975 	struct window_mode_entry	*wme = cs->wme;
1976 	u_int				 np = wme->prefix;
1977 
1978 	for (; np != 0; np--)
1979 		window_copy_pageup1(wme, 0);
1980 	return (WINDOW_COPY_CMD_NOTHING);
1981 }
1982 
1983 static enum window_copy_cmd_action
window_copy_cmd_previous_paragraph(struct window_copy_cmd_state * cs)1984 window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs)
1985 {
1986 	struct window_mode_entry	*wme = cs->wme;
1987 	u_int				 np = wme->prefix;
1988 
1989 	for (; np != 0; np--)
1990 		window_copy_previous_paragraph(wme);
1991 	return (WINDOW_COPY_CMD_NOTHING);
1992 }
1993 
1994 static enum window_copy_cmd_action
window_copy_cmd_previous_space(struct window_copy_cmd_state * cs)1995 window_copy_cmd_previous_space(struct window_copy_cmd_state *cs)
1996 {
1997 	struct window_mode_entry	*wme = cs->wme;
1998 	u_int				 np = wme->prefix;
1999 
2000 	for (; np != 0; np--)
2001 		window_copy_cursor_previous_word(wme, "", 1);
2002 	return (WINDOW_COPY_CMD_NOTHING);
2003 }
2004 
2005 static enum window_copy_cmd_action
window_copy_cmd_previous_word(struct window_copy_cmd_state * cs)2006 window_copy_cmd_previous_word(struct window_copy_cmd_state *cs)
2007 {
2008 	struct window_mode_entry	*wme = cs->wme;
2009 	u_int				 np = wme->prefix;
2010 	const char			*separators;
2011 
2012 	separators = options_get_string(cs->s->options, "word-separators");
2013 
2014 	for (; np != 0; np--)
2015 		window_copy_cursor_previous_word(wme, separators, 1);
2016 	return (WINDOW_COPY_CMD_NOTHING);
2017 }
2018 
2019 static enum window_copy_cmd_action
window_copy_cmd_rectangle_on(struct window_copy_cmd_state * cs)2020 window_copy_cmd_rectangle_on(struct window_copy_cmd_state *cs)
2021 {
2022 	struct window_mode_entry	*wme = cs->wme;
2023 	struct window_copy_mode_data	*data = wme->data;
2024 
2025 	data->lineflag = LINE_SEL_NONE;
2026 	window_copy_rectangle_set(wme, 1);
2027 
2028 	return (WINDOW_COPY_CMD_NOTHING);
2029 }
2030 
2031 static enum window_copy_cmd_action
window_copy_cmd_rectangle_off(struct window_copy_cmd_state * cs)2032 window_copy_cmd_rectangle_off(struct window_copy_cmd_state *cs)
2033 {
2034 	struct window_mode_entry	*wme = cs->wme;
2035 	struct window_copy_mode_data	*data = wme->data;
2036 
2037 	data->lineflag = LINE_SEL_NONE;
2038 	window_copy_rectangle_set(wme, 0);
2039 
2040 	return (WINDOW_COPY_CMD_NOTHING);
2041 }
2042 
2043 static enum window_copy_cmd_action
window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state * cs)2044 window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
2045 {
2046 	struct window_mode_entry	*wme = cs->wme;
2047 	struct window_copy_mode_data	*data = wme->data;
2048 
2049 	data->lineflag = LINE_SEL_NONE;
2050 	window_copy_rectangle_set(wme, !data->rectflag);
2051 
2052 	return (WINDOW_COPY_CMD_NOTHING);
2053 }
2054 
2055 static enum window_copy_cmd_action
window_copy_cmd_scroll_down(struct window_copy_cmd_state * cs)2056 window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
2057 {
2058 	struct window_mode_entry	*wme = cs->wme;
2059 	struct window_copy_mode_data	*data = wme->data;
2060 	u_int				 np = wme->prefix;
2061 
2062 	for (; np != 0; np--)
2063 		window_copy_cursor_down(wme, 1);
2064 	if (data->scroll_exit && data->oy == 0)
2065 		return (WINDOW_COPY_CMD_CANCEL);
2066 	return (WINDOW_COPY_CMD_NOTHING);
2067 }
2068 
2069 static enum window_copy_cmd_action
window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state * cs)2070 window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs)
2071 {
2072 	struct window_mode_entry	*wme = cs->wme;
2073 	struct window_copy_mode_data	*data = wme->data;
2074 	u_int				 np = wme->prefix;
2075 
2076 	for (; np != 0; np--)
2077 		window_copy_cursor_down(wme, 1);
2078 	if (data->oy == 0)
2079 		return (WINDOW_COPY_CMD_CANCEL);
2080 	return (WINDOW_COPY_CMD_NOTHING);
2081 }
2082 
2083 static enum window_copy_cmd_action
window_copy_cmd_scroll_up(struct window_copy_cmd_state * cs)2084 window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
2085 {
2086 	struct window_mode_entry	*wme = cs->wme;
2087 	u_int				 np = wme->prefix;
2088 
2089 	for (; np != 0; np--)
2090 		window_copy_cursor_up(wme, 1);
2091 	return (WINDOW_COPY_CMD_NOTHING);
2092 }
2093 
2094 static enum window_copy_cmd_action
window_copy_cmd_search_again(struct window_copy_cmd_state * cs)2095 window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
2096 {
2097 	struct window_mode_entry	*wme = cs->wme;
2098 	struct window_copy_mode_data	*data = wme->data;
2099 	u_int				 np = wme->prefix;
2100 
2101 	if (data->searchtype == WINDOW_COPY_SEARCHUP) {
2102 		for (; np != 0; np--)
2103 			window_copy_search_up(wme, data->searchregex);
2104 	} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
2105 		for (; np != 0; np--)
2106 			window_copy_search_down(wme, data->searchregex);
2107 	}
2108 	return (WINDOW_COPY_CMD_NOTHING);
2109 }
2110 
2111 static enum window_copy_cmd_action
window_copy_cmd_search_reverse(struct window_copy_cmd_state * cs)2112 window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
2113 {
2114 	struct window_mode_entry	*wme = cs->wme;
2115 	struct window_copy_mode_data	*data = wme->data;
2116 	u_int				 np = wme->prefix;
2117 
2118 	if (data->searchtype == WINDOW_COPY_SEARCHUP) {
2119 		for (; np != 0; np--)
2120 			window_copy_search_down(wme, data->searchregex);
2121 	} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
2122 		for (; np != 0; np--)
2123 			window_copy_search_up(wme, data->searchregex);
2124 	}
2125 	return (WINDOW_COPY_CMD_NOTHING);
2126 }
2127 
2128 static enum window_copy_cmd_action
window_copy_cmd_select_line(struct window_copy_cmd_state * cs)2129 window_copy_cmd_select_line(struct window_copy_cmd_state *cs)
2130 {
2131 	struct window_mode_entry	*wme = cs->wme;
2132 	struct window_copy_mode_data	*data = wme->data;
2133 	u_int				 np = wme->prefix;
2134 
2135 	data->lineflag = LINE_SEL_LEFT_RIGHT;
2136 	data->rectflag = 0;
2137 	data->selflag = SEL_LINE;
2138 	data->dx = data->cx;
2139 	data->dy = screen_hsize(data->backing) + data->cy - data->oy;
2140 
2141 	window_copy_cursor_start_of_line(wme);
2142 	data->selrx = data->cx;
2143 	data->selry = screen_hsize(data->backing) + data->cy - data->oy;
2144 	data->endselry = data->selry;
2145 	window_copy_start_selection(wme);
2146 	window_copy_cursor_end_of_line(wme);
2147 	data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
2148 	data->endselrx = window_copy_find_length(wme, data->endselry);
2149 	for (; np > 1; np--) {
2150 		window_copy_cursor_down(wme, 0);
2151 		window_copy_cursor_end_of_line(wme);
2152 	}
2153 
2154 	return (WINDOW_COPY_CMD_REDRAW);
2155 }
2156 
2157 static enum window_copy_cmd_action
window_copy_cmd_select_word(struct window_copy_cmd_state * cs)2158 window_copy_cmd_select_word(struct window_copy_cmd_state *cs)
2159 {
2160 	struct window_mode_entry	*wme = cs->wme;
2161 	struct options			*session_options = cs->s->options;
2162 	struct window_copy_mode_data	*data = wme->data;
2163 	u_int				 px, py, nextx, nexty;
2164 
2165 	data->lineflag = LINE_SEL_LEFT_RIGHT;
2166 	data->rectflag = 0;
2167 	data->selflag = SEL_WORD;
2168 	data->dx = data->cx;
2169 	data->dy = screen_hsize(data->backing) + data->cy - data->oy;
2170 
2171 	data->separators = options_get_string(session_options,
2172 	    "word-separators");
2173 	window_copy_cursor_previous_word(wme, data->separators, 0);
2174 	px = data->cx;
2175 	py = screen_hsize(data->backing) + data->cy - data->oy;
2176 	data->selrx = px;
2177 	data->selry = py;
2178 	window_copy_start_selection(wme);
2179 
2180 	/* Handle single character words. */
2181 	nextx = px + 1;
2182 	nexty = py;
2183 	if (grid_get_line(data->backing->grid, nexty)->flags &
2184 	    GRID_LINE_WRAPPED && nextx > screen_size_x(data->backing) - 1) {
2185 		nextx = 0;
2186 		nexty++;
2187 	}
2188 	if (px >= window_copy_find_length(wme, py) ||
2189 	    !window_copy_in_set(wme, nextx, nexty, WHITESPACE))
2190 		window_copy_cursor_next_word_end(wme, data->separators, 1);
2191 	else {
2192 		window_copy_update_cursor(wme, px, data->cy);
2193 		if (window_copy_update_selection(wme, 1, 1))
2194 			window_copy_redraw_lines(wme, data->cy, 1);
2195 	}
2196 	data->endselrx = data->cx;
2197 	data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
2198 	if (data->dy > data->endselry) {
2199 		data->dy = data->endselry;
2200 		data->dx = data->endselrx;
2201 	} else if (data->dx > data->endselrx)
2202 		data->dx = data->endselrx;
2203 
2204 	return (WINDOW_COPY_CMD_REDRAW);
2205 }
2206 
2207 static enum window_copy_cmd_action
window_copy_cmd_set_mark(struct window_copy_cmd_state * cs)2208 window_copy_cmd_set_mark(struct window_copy_cmd_state *cs)
2209 {
2210 	struct window_copy_mode_data	*data = cs->wme->data;
2211 
2212 	data->mx = data->cx;
2213 	data->my = screen_hsize(data->backing) + data->cy - data->oy;
2214 	data->showmark = 1;
2215 	return (WINDOW_COPY_CMD_REDRAW);
2216 }
2217 
2218 static enum window_copy_cmd_action
window_copy_cmd_start_of_line(struct window_copy_cmd_state * cs)2219 window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs)
2220 {
2221 	struct window_mode_entry	*wme = cs->wme;
2222 
2223 	window_copy_cursor_start_of_line(wme);
2224 	return (WINDOW_COPY_CMD_NOTHING);
2225 }
2226 
2227 static enum window_copy_cmd_action
window_copy_cmd_top_line(struct window_copy_cmd_state * cs)2228 window_copy_cmd_top_line(struct window_copy_cmd_state *cs)
2229 {
2230 	struct window_mode_entry	*wme = cs->wme;
2231 	struct window_copy_mode_data	*data = wme->data;
2232 
2233 	data->cx = 0;
2234 	data->cy = 0;
2235 
2236 	window_copy_update_selection(wme, 1, 0);
2237 	return (WINDOW_COPY_CMD_REDRAW);
2238 }
2239 
2240 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state * cs)2241 window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs)
2242 {
2243 	struct window_mode_entry	*wme = cs->wme;
2244 	struct client			*c = cs->c;
2245 	struct session			*s = cs->s;
2246 	struct winlink			*wl = cs->wl;
2247 	struct window_pane		*wp = wme->wp;
2248 	char				*command = NULL, *prefix = NULL;
2249 	const char			*arg0 = args_string(cs->wargs, 0);
2250 	const char			*arg1 = args_string(cs->wargs, 1);
2251 	int				 set_paste = !args_has(cs->wargs, 'P');
2252 	int				 set_clip = !args_has(cs->wargs, 'C');
2253 
2254 	if (arg1 != NULL)
2255 		prefix = format_single(NULL, arg1, c, s, wl, wp);
2256 
2257 	if (s != NULL && arg0 != NULL && *arg0 != '\0')
2258 		command = format_single(NULL, arg0, c, s, wl, wp);
2259 	window_copy_copy_pipe(wme, s, prefix, command,
2260 	    set_paste, set_clip);
2261 	free(command);
2262 
2263 	free(prefix);
2264 	return (WINDOW_COPY_CMD_NOTHING);
2265 }
2266 
2267 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe(struct window_copy_cmd_state * cs)2268 window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs)
2269 {
2270 	struct window_mode_entry	*wme = cs->wme;
2271 
2272 	window_copy_cmd_copy_pipe_no_clear(cs);
2273 	window_copy_clear_selection(wme);
2274 	return (WINDOW_COPY_CMD_REDRAW);
2275 }
2276 
2277 static enum window_copy_cmd_action
window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state * cs)2278 window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs)
2279 {
2280 	struct window_mode_entry	*wme = cs->wme;
2281 
2282 	window_copy_cmd_copy_pipe_no_clear(cs);
2283 	window_copy_clear_selection(wme);
2284 	return (WINDOW_COPY_CMD_CANCEL);
2285 }
2286 
2287 static enum window_copy_cmd_action
window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state * cs)2288 window_copy_cmd_pipe_no_clear(struct window_copy_cmd_state *cs)
2289 {
2290 	struct window_mode_entry	*wme = cs->wme;
2291 	struct client			*c = cs->c;
2292 	struct session			*s = cs->s;
2293 	struct winlink			*wl = cs->wl;
2294 	struct window_pane		*wp = wme->wp;
2295 	char				*command = NULL;
2296 	const char			*arg0 = args_string(cs->wargs, 0);
2297 
2298 	if (s != NULL && arg0 != NULL && *arg0 != '\0')
2299 		command = format_single(NULL, arg0, c, s, wl, wp);
2300 	window_copy_pipe(wme, s, command);
2301 	free(command);
2302 
2303 	return (WINDOW_COPY_CMD_NOTHING);
2304 }
2305 
2306 static enum window_copy_cmd_action
window_copy_cmd_pipe(struct window_copy_cmd_state * cs)2307 window_copy_cmd_pipe(struct window_copy_cmd_state *cs)
2308 {
2309 	struct window_mode_entry	*wme = cs->wme;
2310 
2311 	window_copy_cmd_pipe_no_clear(cs);
2312 	window_copy_clear_selection(wme);
2313 	return (WINDOW_COPY_CMD_REDRAW);
2314 }
2315 
2316 static enum window_copy_cmd_action
window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state * cs)2317 window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
2318 {
2319 	struct window_mode_entry	*wme = cs->wme;
2320 
2321 	window_copy_cmd_pipe_no_clear(cs);
2322 	window_copy_clear_selection(wme);
2323 	return (WINDOW_COPY_CMD_CANCEL);
2324 }
2325 
2326 static enum window_copy_cmd_action
window_copy_cmd_goto_line(struct window_copy_cmd_state * cs)2327 window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
2328 {
2329 	struct window_mode_entry	*wme = cs->wme;
2330 	const char			*arg0 = args_string(cs->wargs, 0);
2331 
2332 	if (*arg0 != '\0')
2333 		window_copy_goto_line(wme, arg0);
2334 	return (WINDOW_COPY_CMD_NOTHING);
2335 }
2336 
2337 static enum window_copy_cmd_action
window_copy_cmd_jump_backward(struct window_copy_cmd_state * cs)2338 window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs)
2339 {
2340 	struct window_mode_entry	*wme = cs->wme;
2341 	struct window_copy_mode_data	*data = wme->data;
2342 	u_int				 np = wme->prefix;
2343 	const char			*arg0 = args_string(cs->wargs, 0);
2344 
2345 	if (*arg0 != '\0') {
2346 		data->jumptype = WINDOW_COPY_JUMPBACKWARD;
2347 		free(data->jumpchar);
2348 		data->jumpchar = utf8_fromcstr(arg0);
2349 		for (; np != 0; np--)
2350 			window_copy_cursor_jump_back(wme);
2351 	}
2352 	return (WINDOW_COPY_CMD_NOTHING);
2353 }
2354 
2355 static enum window_copy_cmd_action
window_copy_cmd_jump_forward(struct window_copy_cmd_state * cs)2356 window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs)
2357 {
2358 	struct window_mode_entry	*wme = cs->wme;
2359 	struct window_copy_mode_data	*data = wme->data;
2360 	u_int				 np = wme->prefix;
2361 	const char			*arg0 = args_string(cs->wargs, 0);
2362 
2363 	if (*arg0 != '\0') {
2364 		data->jumptype = WINDOW_COPY_JUMPFORWARD;
2365 		free(data->jumpchar);
2366 		data->jumpchar = utf8_fromcstr(arg0);
2367 		for (; np != 0; np--)
2368 			window_copy_cursor_jump(wme);
2369 	}
2370 	return (WINDOW_COPY_CMD_NOTHING);
2371 }
2372 
2373 static enum window_copy_cmd_action
window_copy_cmd_jump_to_backward(struct window_copy_cmd_state * cs)2374 window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs)
2375 {
2376 	struct window_mode_entry	*wme = cs->wme;
2377 	struct window_copy_mode_data	*data = wme->data;
2378 	u_int				 np = wme->prefix;
2379 	const char			*arg0 = args_string(cs->wargs, 0);
2380 
2381 	if (*arg0 != '\0') {
2382 		data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
2383 		free(data->jumpchar);
2384 		data->jumpchar = utf8_fromcstr(arg0);
2385 		for (; np != 0; np--)
2386 			window_copy_cursor_jump_to_back(wme);
2387 	}
2388 	return (WINDOW_COPY_CMD_NOTHING);
2389 }
2390 
2391 static enum window_copy_cmd_action
window_copy_cmd_jump_to_forward(struct window_copy_cmd_state * cs)2392 window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs)
2393 {
2394 	struct window_mode_entry	*wme = cs->wme;
2395 	struct window_copy_mode_data	*data = wme->data;
2396 	u_int				 np = wme->prefix;
2397 	const char			*arg0 = args_string(cs->wargs, 0);
2398 
2399 	if (*arg0 != '\0') {
2400 		data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
2401 		free(data->jumpchar);
2402 		data->jumpchar = utf8_fromcstr(arg0);
2403 		for (; np != 0; np--)
2404 			window_copy_cursor_jump_to(wme);
2405 	}
2406 	return (WINDOW_COPY_CMD_NOTHING);
2407 }
2408 
2409 static enum window_copy_cmd_action
window_copy_cmd_jump_to_mark(struct window_copy_cmd_state * cs)2410 window_copy_cmd_jump_to_mark(struct window_copy_cmd_state *cs)
2411 {
2412 	struct window_mode_entry	*wme = cs->wme;
2413 
2414 	window_copy_jump_to_mark(wme);
2415 	return (WINDOW_COPY_CMD_NOTHING);
2416 }
2417 
2418 static enum window_copy_cmd_action
window_copy_cmd_next_prompt(struct window_copy_cmd_state * cs)2419 window_copy_cmd_next_prompt(struct window_copy_cmd_state *cs)
2420 {
2421 	struct window_mode_entry	*wme = cs->wme;
2422 
2423 	window_copy_cursor_prompt(wme, 1, args_has(cs->wargs, 'o'));
2424 	return (WINDOW_COPY_CMD_NOTHING);
2425 }
2426 
2427 static enum window_copy_cmd_action
window_copy_cmd_previous_prompt(struct window_copy_cmd_state * cs)2428 window_copy_cmd_previous_prompt(struct window_copy_cmd_state *cs)
2429 {
2430 	struct window_mode_entry	*wme = cs->wme;
2431 
2432 	window_copy_cursor_prompt(wme, 0, args_has(cs->wargs, 'o'));
2433 	return (WINDOW_COPY_CMD_NOTHING);
2434 }
2435 
2436 static enum window_copy_cmd_action
window_copy_cmd_search_backward(struct window_copy_cmd_state * cs)2437 window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
2438 {
2439 	struct window_mode_entry	*wme = cs->wme;
2440 	struct window_copy_mode_data	*data = wme->data;
2441 	u_int				 np = wme->prefix;
2442 
2443 	if (!window_copy_expand_search_string(cs))
2444 		return (WINDOW_COPY_CMD_NOTHING);
2445 
2446 	if (data->searchstr != NULL) {
2447 		data->searchtype = WINDOW_COPY_SEARCHUP;
2448 		data->searchregex = 1;
2449 		data->timeout = 0;
2450 		for (; np != 0; np--)
2451 			window_copy_search_up(wme, 1);
2452 	}
2453 	return (WINDOW_COPY_CMD_NOTHING);
2454 }
2455 
2456 static enum window_copy_cmd_action
window_copy_cmd_search_backward_text(struct window_copy_cmd_state * cs)2457 window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
2458 {
2459 	struct window_mode_entry	*wme = cs->wme;
2460 	struct window_copy_mode_data	*data = wme->data;
2461 	u_int				 np = wme->prefix;
2462 
2463 	if (!window_copy_expand_search_string(cs))
2464 		return (WINDOW_COPY_CMD_NOTHING);
2465 
2466 	if (data->searchstr != NULL) {
2467 		data->searchtype = WINDOW_COPY_SEARCHUP;
2468 		data->searchregex = 0;
2469 		data->timeout = 0;
2470 		for (; np != 0; np--)
2471 			window_copy_search_up(wme, 0);
2472 	}
2473 	return (WINDOW_COPY_CMD_NOTHING);
2474 }
2475 
2476 static enum window_copy_cmd_action
window_copy_cmd_search_forward(struct window_copy_cmd_state * cs)2477 window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
2478 {
2479 	struct window_mode_entry	*wme = cs->wme;
2480 	struct window_copy_mode_data	*data = wme->data;
2481 	u_int				 np = wme->prefix;
2482 
2483 	if (!window_copy_expand_search_string(cs))
2484 		return (WINDOW_COPY_CMD_NOTHING);
2485 
2486 	if (data->searchstr != NULL) {
2487 		data->searchtype = WINDOW_COPY_SEARCHDOWN;
2488 		data->searchregex = 1;
2489 		data->timeout = 0;
2490 		for (; np != 0; np--)
2491 			window_copy_search_down(wme, 1);
2492 	}
2493 	return (WINDOW_COPY_CMD_NOTHING);
2494 }
2495 
2496 static enum window_copy_cmd_action
window_copy_cmd_search_forward_text(struct window_copy_cmd_state * cs)2497 window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs)
2498 {
2499 	struct window_mode_entry	*wme = cs->wme;
2500 	struct window_copy_mode_data	*data = wme->data;
2501 	u_int				 np = wme->prefix;
2502 
2503 	if (!window_copy_expand_search_string(cs))
2504 		return (WINDOW_COPY_CMD_NOTHING);
2505 
2506 	if (data->searchstr != NULL) {
2507 		data->searchtype = WINDOW_COPY_SEARCHDOWN;
2508 		data->searchregex = 0;
2509 		data->timeout = 0;
2510 		for (; np != 0; np--)
2511 			window_copy_search_down(wme, 0);
2512 	}
2513 	return (WINDOW_COPY_CMD_NOTHING);
2514 }
2515 
2516 static enum window_copy_cmd_action
window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state * cs)2517 window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
2518 {
2519 	struct window_mode_entry	*wme = cs->wme;
2520 	struct window_copy_mode_data	*data = wme->data;
2521 	const char			*arg0 = args_string(cs->wargs, 0);
2522 	const char			*ss = data->searchstr;
2523 	char				 prefix;
2524 	enum window_copy_cmd_action	 action = WINDOW_COPY_CMD_NOTHING;
2525 
2526 	data->timeout = 0;
2527 
2528 	log_debug("%s: %s", __func__, arg0);
2529 
2530 	prefix = *arg0++;
2531 	if (data->searchx == -1 || data->searchy == -1) {
2532 		data->searchx = data->cx;
2533 		data->searchy = data->cy;
2534 		data->searcho = data->oy;
2535 	} else if (ss != NULL && strcmp(arg0, ss) != 0) {
2536 		data->cx = data->searchx;
2537 		data->cy = data->searchy;
2538 		data->oy = data->searcho;
2539 		action = WINDOW_COPY_CMD_REDRAW;
2540 	}
2541 	if (*arg0 == '\0') {
2542 		window_copy_clear_marks(wme);
2543 		return (WINDOW_COPY_CMD_REDRAW);
2544 	}
2545 	switch (prefix) {
2546 	case '=':
2547 	case '-':
2548 		data->searchtype = WINDOW_COPY_SEARCHUP;
2549 		data->searchregex = 0;
2550 		free(data->searchstr);
2551 		data->searchstr = xstrdup(arg0);
2552 		if (!window_copy_search_up(wme, 0)) {
2553 			window_copy_clear_marks(wme);
2554 			return (WINDOW_COPY_CMD_REDRAW);
2555 		}
2556 		break;
2557 	case '+':
2558 		data->searchtype = WINDOW_COPY_SEARCHDOWN;
2559 		data->searchregex = 0;
2560 		free(data->searchstr);
2561 		data->searchstr = xstrdup(arg0);
2562 		if (!window_copy_search_down(wme, 0)) {
2563 			window_copy_clear_marks(wme);
2564 			return (WINDOW_COPY_CMD_REDRAW);
2565 		}
2566 		break;
2567 	}
2568 	return (action);
2569 }
2570 
2571 static enum window_copy_cmd_action
window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state * cs)2572 window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
2573 {
2574 	struct window_mode_entry	*wme = cs->wme;
2575 	struct window_copy_mode_data	*data = wme->data;
2576 	const char			*arg0 = args_string(cs->wargs, 0);
2577 	const char			*ss = data->searchstr;
2578 	char				 prefix;
2579 	enum window_copy_cmd_action	 action = WINDOW_COPY_CMD_NOTHING;
2580 
2581 	data->timeout = 0;
2582 
2583 	log_debug("%s: %s", __func__, arg0);
2584 
2585 	prefix = *arg0++;
2586 	if (data->searchx == -1 || data->searchy == -1) {
2587 		data->searchx = data->cx;
2588 		data->searchy = data->cy;
2589 		data->searcho = data->oy;
2590 	} else if (ss != NULL && strcmp(arg0, ss) != 0) {
2591 		data->cx = data->searchx;
2592 		data->cy = data->searchy;
2593 		data->oy = data->searcho;
2594 		action = WINDOW_COPY_CMD_REDRAW;
2595 	}
2596 	if (*arg0 == '\0') {
2597 		window_copy_clear_marks(wme);
2598 		return (WINDOW_COPY_CMD_REDRAW);
2599 	}
2600 	switch (prefix) {
2601 	case '=':
2602 	case '+':
2603 		data->searchtype = WINDOW_COPY_SEARCHDOWN;
2604 		data->searchregex = 0;
2605 		free(data->searchstr);
2606 		data->searchstr = xstrdup(arg0);
2607 		if (!window_copy_search_down(wme, 0)) {
2608 			window_copy_clear_marks(wme);
2609 			return (WINDOW_COPY_CMD_REDRAW);
2610 		}
2611 		break;
2612 	case '-':
2613 		data->searchtype = WINDOW_COPY_SEARCHUP;
2614 		data->searchregex = 0;
2615 		free(data->searchstr);
2616 		data->searchstr = xstrdup(arg0);
2617 		if (!window_copy_search_up(wme, 0)) {
2618 			window_copy_clear_marks(wme);
2619 			return (WINDOW_COPY_CMD_REDRAW);
2620 		}
2621 	}
2622 	return (action);
2623 }
2624 
2625 static enum window_copy_cmd_action
window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state * cs)2626 window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs)
2627 {
2628 	struct window_mode_entry	*wme = cs->wme;
2629 	struct window_pane		*wp = wme->swp;
2630 	struct window_copy_mode_data	*data = wme->data;
2631 
2632 	if (data->viewmode)
2633 		return (WINDOW_COPY_CMD_NOTHING);
2634 
2635 	screen_free(data->backing);
2636 	free(data->backing);
2637 	data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL,
2638 	    NULL, wme->swp != wme->wp);
2639 
2640 	window_copy_size_changed(wme);
2641 	return (WINDOW_COPY_CMD_REDRAW);
2642 }
2643 
2644 static const struct {
2645 	const char			 *command;
2646 	u_int				  minargs;
2647 	u_int				  maxargs;
2648 	struct args_parse		  args;
2649 	enum window_copy_cmd_clear	  clear;
2650 	enum window_copy_cmd_action	(*f)(struct window_copy_cmd_state *);
2651 } window_copy_cmd_table[] = {
2652 	{ .command = "append-selection",
2653 	  .args = { "", 0, 0, NULL },
2654 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2655 	  .f = window_copy_cmd_append_selection
2656 	},
2657 	{ .command = "append-selection-and-cancel",
2658 	  .args = { "", 0, 0, NULL },
2659 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2660 	  .f = window_copy_cmd_append_selection_and_cancel
2661 	},
2662 	{ .command = "back-to-indentation",
2663 	  .args = { "", 0, 0, NULL },
2664 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2665 	  .f = window_copy_cmd_back_to_indentation
2666 	},
2667 	{ .command = "begin-selection",
2668 	  .args = { "", 0, 0, NULL },
2669 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2670 	  .f = window_copy_cmd_begin_selection
2671 	},
2672 	{ .command = "bottom-line",
2673 	  .args = { "", 0, 0, NULL },
2674 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2675 	  .f = window_copy_cmd_bottom_line
2676 	},
2677 	{ .command = "cancel",
2678 	  .args = { "", 0, 0, NULL },
2679 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2680 	  .f = window_copy_cmd_cancel
2681 	},
2682 	{ .command = "clear-selection",
2683 	  .args = { "", 0, 0, NULL },
2684 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2685 	  .f = window_copy_cmd_clear_selection
2686 	},
2687 	{ .command = "copy-end-of-line",
2688 	  .args = { "CP", 0, 1, NULL },
2689 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2690 	  .f = window_copy_cmd_copy_end_of_line
2691 	},
2692 	{ .command = "copy-end-of-line-and-cancel",
2693 	  .args = { "CP", 0, 1, NULL },
2694 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2695 	  .f = window_copy_cmd_copy_end_of_line_and_cancel
2696 	},
2697 	{ .command = "copy-pipe-end-of-line",
2698 	  .args = { "CP", 0, 2, NULL },
2699 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2700 	  .f = window_copy_cmd_copy_pipe_end_of_line
2701 	},
2702 	{ .command = "copy-pipe-end-of-line-and-cancel",
2703 	  .args = { "CP", 0, 2, NULL },
2704 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2705 	  .f = window_copy_cmd_copy_pipe_end_of_line_and_cancel
2706 	},
2707 	{ .command = "copy-line",
2708 	  .args = { "CP", 0, 1, NULL },
2709 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2710 	  .f = window_copy_cmd_copy_line
2711 	},
2712 	{ .command = "copy-line-and-cancel",
2713 	  .args = { "CP", 0, 1, NULL },
2714 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2715 	  .f = window_copy_cmd_copy_line_and_cancel
2716 	},
2717 	{ .command = "copy-pipe-line",
2718 	  .args = { "CP", 0, 2, NULL },
2719 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2720 	  .f = window_copy_cmd_copy_pipe_line
2721 	},
2722 	{ .command = "copy-pipe-line-and-cancel",
2723 	  .args = { "CP", 0, 2, NULL },
2724 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2725 	  .f = window_copy_cmd_copy_pipe_line_and_cancel
2726 	},
2727 	{ .command = "copy-pipe-no-clear",
2728 	  .args = { "CP", 0, 2, NULL },
2729 	  .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2730 	  .f = window_copy_cmd_copy_pipe_no_clear
2731 	},
2732 	{ .command = "copy-pipe",
2733 	  .args = { "CP", 0, 2, NULL },
2734 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2735 	  .f = window_copy_cmd_copy_pipe
2736 	},
2737 	{ .command = "copy-pipe-and-cancel",
2738 	  .args = { "CP", 0, 2, NULL },
2739 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2740 	  .f = window_copy_cmd_copy_pipe_and_cancel
2741 	},
2742 	{ .command = "copy-selection-no-clear",
2743 	  .args = { "CP", 0, 1, NULL },
2744 	  .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2745 	  .f = window_copy_cmd_copy_selection_no_clear
2746 	},
2747 	{ .command = "copy-selection",
2748 	  .args = { "CP", 0, 1, NULL },
2749 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2750 	  .f = window_copy_cmd_copy_selection
2751 	},
2752 	{ .command = "copy-selection-and-cancel",
2753 	  .args = { "CP", 0, 1, NULL },
2754 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2755 	  .f = window_copy_cmd_copy_selection_and_cancel
2756 	},
2757 	{ .command = "cursor-down",
2758 	  .args = { "", 0, 0, NULL },
2759 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2760 	  .f = window_copy_cmd_cursor_down
2761 	},
2762 	{ .command = "cursor-down-and-cancel",
2763 	  .args = { "", 0, 0, NULL },
2764 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2765 	  .f = window_copy_cmd_cursor_down_and_cancel
2766 	},
2767 	{ .command = "cursor-left",
2768 	  .args = { "", 0, 0, NULL },
2769 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2770 	  .f = window_copy_cmd_cursor_left
2771 	},
2772 	{ .command = "cursor-right",
2773 	  .args = { "", 0, 0, NULL },
2774 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2775 	  .f = window_copy_cmd_cursor_right
2776 	},
2777 	{ .command = "cursor-up",
2778 	  .args = { "", 0, 0, NULL },
2779 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2780 	  .f = window_copy_cmd_cursor_up
2781 	},
2782 	{ .command = "end-of-line",
2783 	  .args = { "", 0, 0, NULL },
2784 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2785 	  .f = window_copy_cmd_end_of_line
2786 	},
2787 	{ .command = "goto-line",
2788 	  .args = { "", 1, 1, NULL },
2789 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2790 	  .f = window_copy_cmd_goto_line
2791 	},
2792 	{ .command = "halfpage-down",
2793 	  .args = { "", 0, 0, NULL },
2794 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2795 	  .f = window_copy_cmd_halfpage_down
2796 	},
2797 	{ .command = "halfpage-down-and-cancel",
2798 	  .args = { "", 0, 0, NULL },
2799 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2800 	  .f = window_copy_cmd_halfpage_down_and_cancel
2801 	},
2802 	{ .command = "halfpage-up",
2803 	  .args = { "", 0, 0, NULL },
2804 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2805 	  .f = window_copy_cmd_halfpage_up
2806 	},
2807 	{ .command = "history-bottom",
2808 	  .args = { "", 0, 0, NULL },
2809 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2810 	  .f = window_copy_cmd_history_bottom
2811 	},
2812 	{ .command = "history-top",
2813 	  .args = { "", 0, 0, NULL },
2814 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2815 	  .f = window_copy_cmd_history_top
2816 	},
2817 	{ .command = "jump-again",
2818 	  .args = { "", 0, 0, NULL },
2819 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2820 	  .f = window_copy_cmd_jump_again
2821 	},
2822 	{ .command = "jump-backward",
2823 	  .args = { "", 1, 1, NULL },
2824 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2825 	  .f = window_copy_cmd_jump_backward
2826 	},
2827 	{ .command = "jump-forward",
2828 	  .args = { "", 1, 1, NULL },
2829 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2830 	  .f = window_copy_cmd_jump_forward
2831 	},
2832 	{ .command = "jump-reverse",
2833 	  .args = { "", 0, 0, NULL },
2834 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2835 	  .f = window_copy_cmd_jump_reverse
2836 	},
2837 	{ .command = "jump-to-backward",
2838 	  .args = { "", 1, 1, NULL },
2839 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2840 	  .f = window_copy_cmd_jump_to_backward
2841 	},
2842 	{ .command = "jump-to-forward",
2843 	  .args = { "", 1, 1, NULL },
2844 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2845 	  .f = window_copy_cmd_jump_to_forward
2846 	},
2847 	{ .command = "jump-to-mark",
2848 	  .args = { "", 0, 0, NULL },
2849 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2850 	  .f = window_copy_cmd_jump_to_mark
2851 	},
2852 	{ .command = "next-prompt",
2853 	  .args = { "o", 0, 0, NULL },
2854 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2855 	  .f = window_copy_cmd_next_prompt
2856 	},
2857 	{ .command = "previous-prompt",
2858 	  .args = { "o", 0, 0, NULL },
2859 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2860 	  .f = window_copy_cmd_previous_prompt
2861 	},
2862 	{ .command = "middle-line",
2863 	  .args = { "", 0, 0, NULL },
2864 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2865 	  .f = window_copy_cmd_middle_line
2866 	},
2867 	{ .command = "next-matching-bracket",
2868 	  .args = { "", 0, 0, NULL },
2869 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2870 	  .f = window_copy_cmd_next_matching_bracket
2871 	},
2872 	{ .command = "next-paragraph",
2873 	  .args = { "", 0, 0, NULL },
2874 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2875 	  .f = window_copy_cmd_next_paragraph
2876 	},
2877 	{ .command = "next-space",
2878 	  .args = { "", 0, 0, NULL },
2879 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2880 	  .f = window_copy_cmd_next_space
2881 	},
2882 	{ .command = "next-space-end",
2883 	  .args = { "", 0, 0, NULL },
2884 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2885 	  .f = window_copy_cmd_next_space_end
2886 	},
2887 	{ .command = "next-word",
2888 	  .args = { "", 0, 0, NULL },
2889 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2890 	  .f = window_copy_cmd_next_word
2891 	},
2892 	{ .command = "next-word-end",
2893 	  .args = { "", 0, 0, NULL },
2894 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2895 	  .f = window_copy_cmd_next_word_end
2896 	},
2897 	{ .command = "other-end",
2898 	  .args = { "", 0, 0, NULL },
2899 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2900 	  .f = window_copy_cmd_other_end
2901 	},
2902 	{ .command = "page-down",
2903 	  .args = { "", 0, 0, NULL },
2904 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2905 	  .f = window_copy_cmd_page_down
2906 	},
2907 	{ .command = "page-down-and-cancel",
2908 	  .args = { "", 0, 0, NULL },
2909 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2910 	  .f = window_copy_cmd_page_down_and_cancel
2911 	},
2912 	{ .command = "page-up",
2913 	  .args = { "", 0, 0, NULL },
2914 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2915 	  .f = window_copy_cmd_page_up
2916 	},
2917 	{ .command = "pipe-no-clear",
2918 	  .args = { "", 0, 1, NULL },
2919 	  .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
2920 	  .f = window_copy_cmd_pipe_no_clear
2921 	},
2922 	{ .command = "pipe",
2923 	  .args = { "", 0, 1, NULL },
2924 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2925 	  .f = window_copy_cmd_pipe
2926 	},
2927 	{ .command = "pipe-and-cancel",
2928 	  .args = { "", 0, 1, NULL },
2929 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2930 	  .f = window_copy_cmd_pipe_and_cancel
2931 	},
2932 	{ .command = "previous-matching-bracket",
2933 	  .args = { "", 0, 0, NULL },
2934 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2935 	  .f = window_copy_cmd_previous_matching_bracket
2936 	},
2937 	{ .command = "previous-paragraph",
2938 	  .args = { "", 0, 0, NULL },
2939 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2940 	  .f = window_copy_cmd_previous_paragraph
2941 	},
2942 	{ .command = "previous-space",
2943 	  .args = { "", 0, 0, NULL },
2944 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2945 	  .f = window_copy_cmd_previous_space
2946 	},
2947 	{ .command = "previous-word",
2948 	  .args = { "", 0, 0, NULL },
2949 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2950 	  .f = window_copy_cmd_previous_word
2951 	},
2952 	{ .command = "rectangle-on",
2953 	  .args = { "", 0, 0, NULL },
2954 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2955 	  .f = window_copy_cmd_rectangle_on
2956 	},
2957 	{ .command = "rectangle-off",
2958 	  .args = { "", 0, 0, NULL },
2959 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2960 	  .f = window_copy_cmd_rectangle_off
2961 	},
2962 	{ .command = "rectangle-toggle",
2963 	  .args = { "", 0, 0, NULL },
2964 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2965 	  .f = window_copy_cmd_rectangle_toggle
2966 	},
2967 	{ .command = "refresh-from-pane",
2968 	  .args = { "", 0, 0, NULL },
2969 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2970 	  .f = window_copy_cmd_refresh_from_pane
2971 	},
2972 	{ .command = "scroll-bottom",
2973 	  .args = { "", 0, 0, NULL },
2974 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2975 	  .f = window_copy_cmd_scroll_bottom
2976 	},
2977 	{ .command = "scroll-down",
2978 	  .args = { "", 0, 0, NULL },
2979 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
2980 	  .f = window_copy_cmd_scroll_down
2981 	},
2982 	{ .command = "scroll-down-and-cancel",
2983 	  .args = { "", 0, 0, NULL },
2984 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2985 	  .f = window_copy_cmd_scroll_down_and_cancel
2986 	},
2987 	{ .command = "scroll-middle",
2988 	  .args = { "", 0, 0, NULL },
2989 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2990 	  .f = window_copy_cmd_scroll_middle
2991 	},
2992 	{ .command = "scroll-top",
2993 	  .args = { "", 0, 0, NULL },
2994 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
2995 	  .f = window_copy_cmd_scroll_top
2996 	},
2997 	{ .command = "scroll-up",
2998 	  .args = { "", 0, 0, NULL },
2999 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
3000 	  .f = window_copy_cmd_scroll_up
3001 	},
3002 	{ .command = "search-again",
3003 	  .args = { "", 0, 0, NULL },
3004 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3005 	  .f = window_copy_cmd_search_again
3006 	},
3007 	{ .command = "search-backward",
3008 	  .args = { "", 0, 1, NULL },
3009 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3010 	  .f = window_copy_cmd_search_backward
3011 	},
3012 	{ .command = "search-backward-text",
3013 	  .args = { "", 0, 1, NULL },
3014 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3015 	  .f = window_copy_cmd_search_backward_text
3016 	},
3017 	{ .command = "search-backward-incremental",
3018 	  .args = { "", 1, 1, NULL },
3019 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3020 	  .f = window_copy_cmd_search_backward_incremental
3021 	},
3022 	{ .command = "search-forward",
3023 	  .args = { "", 0, 1, NULL },
3024 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3025 	  .f = window_copy_cmd_search_forward
3026 	},
3027 	{ .command = "search-forward-text",
3028 	  .args = { "", 0, 1, NULL },
3029 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3030 	  .f = window_copy_cmd_search_forward_text
3031 	},
3032 	{ .command = "search-forward-incremental",
3033 	  .args = { "", 1, 1, NULL },
3034 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3035 	  .f = window_copy_cmd_search_forward_incremental
3036 	},
3037 	{ .command = "search-reverse",
3038 	  .args = { "", 0, 0, NULL },
3039 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3040 	  .f = window_copy_cmd_search_reverse
3041 	},
3042 	{ .command = "select-line",
3043 	  .args = { "", 0, 0, NULL },
3044 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3045 	  .f = window_copy_cmd_select_line
3046 	},
3047 	{ .command = "select-word",
3048 	  .args = { "", 0, 0, NULL },
3049 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3050 	  .f = window_copy_cmd_select_word
3051 	},
3052 	{ .command = "set-mark",
3053 	  .args = { "", 0, 0, NULL },
3054 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3055 	  .f = window_copy_cmd_set_mark
3056 	},
3057 	{ .command = "start-of-line",
3058 	  .args = { "", 0, 0, NULL },
3059 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
3060 	  .f = window_copy_cmd_start_of_line
3061 	},
3062 	{ .command = "stop-selection",
3063 	  .args = { "", 0, 0, NULL },
3064 	  .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
3065 	  .f = window_copy_cmd_stop_selection
3066 	},
3067 	{ .command = "toggle-position",
3068 	  .args = { "", 0, 0, NULL },
3069 	  .clear = WINDOW_COPY_CMD_CLEAR_NEVER,
3070 	  .f = window_copy_cmd_toggle_position
3071 	},
3072 	{ .command = "top-line",
3073 	  .args = { "", 0, 0, NULL },
3074 	  .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
3075 	  .f = window_copy_cmd_top_line
3076 	}
3077 };
3078 
3079 static void
window_copy_command(struct window_mode_entry * wme,struct client * c,struct session * s,struct winlink * wl,struct args * args,struct mouse_event * m)3080 window_copy_command(struct window_mode_entry *wme, struct client *c,
3081     struct session *s, struct winlink *wl, struct args *args,
3082     struct mouse_event *m)
3083 {
3084 	struct window_copy_mode_data	*data = wme->data;
3085 	struct window_pane		*wp = wme->wp;
3086 	struct window_copy_cmd_state	 cs;
3087 	enum window_copy_cmd_action	 action;
3088 	enum window_copy_cmd_clear	 clear = WINDOW_COPY_CMD_CLEAR_NEVER;
3089 	const char			*command;
3090 	u_int				 i, count = args_count(args);
3091 	int				 keys;
3092 	char				*error = NULL;
3093 
3094 	if (count == 0)
3095 		return;
3096 	command = args_string(args, 0);
3097 
3098 	if (m != NULL && m->valid && !MOUSE_WHEEL(m->b))
3099 		window_copy_move_mouse(m);
3100 
3101 	cs.wme = wme;
3102 	cs.args = args;
3103 	cs.wargs = NULL;
3104 	cs.m = m;
3105 
3106 	cs.c = c;
3107 	cs.s = s;
3108 	cs.wl = wl;
3109 
3110 	action = WINDOW_COPY_CMD_NOTHING;
3111 	for (i = 0; i < nitems(window_copy_cmd_table); i++) {
3112 		if (strcmp(window_copy_cmd_table[i].command, command) == 0) {
3113 			cs.wargs = args_parse(&window_copy_cmd_table[i].args,
3114 			    args_values(args), count, &error);
3115 
3116 			if (error != NULL) {
3117 				free(error);
3118 				error = NULL;
3119 			}
3120 			if (cs.wargs == NULL)
3121 				break;
3122 
3123 			clear = window_copy_cmd_table[i].clear;
3124 			action = window_copy_cmd_table[i].f(&cs);
3125 			args_free(cs.wargs);
3126 			cs.wargs = NULL;
3127 			break;
3128 		}
3129 	}
3130 
3131 	if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
3132 		keys = options_get_number(wp->window->options, "mode-keys");
3133 		if (clear == WINDOW_COPY_CMD_CLEAR_EMACS_ONLY &&
3134 		    keys == MODEKEY_VI)
3135 			clear = WINDOW_COPY_CMD_CLEAR_NEVER;
3136 		if (clear != WINDOW_COPY_CMD_CLEAR_NEVER) {
3137 			window_copy_clear_marks(wme);
3138 			data->searchx = data->searchy = -1;
3139 		}
3140 		if (action == WINDOW_COPY_CMD_NOTHING)
3141 			action = WINDOW_COPY_CMD_REDRAW;
3142 	}
3143 	wme->prefix = 1;
3144 
3145 	if (action == WINDOW_COPY_CMD_CANCEL)
3146 		window_pane_reset_mode(wp);
3147 	else if (action == WINDOW_COPY_CMD_REDRAW)
3148 		window_copy_redraw_screen(wme);
3149 }
3150 
3151 static void
window_copy_scroll_to(struct window_mode_entry * wme,u_int px,u_int py,int no_redraw)3152 window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py,
3153     int no_redraw)
3154 {
3155 	struct window_copy_mode_data	*data = wme->data;
3156 	struct grid			*gd = data->backing->grid;
3157 	u_int				 offset, gap;
3158 
3159 	data->cx = px;
3160 
3161 	if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy)
3162 		data->cy = py - (gd->hsize - data->oy);
3163 	else {
3164 		gap = gd->sy / 4;
3165 		if (py < gd->sy) {
3166 			offset = 0;
3167 			data->cy = py;
3168 		} else if (py > gd->hsize + gd->sy - gap) {
3169 			offset = gd->hsize;
3170 			data->cy = py - gd->hsize;
3171 		} else {
3172 			offset = py + gap - gd->sy;
3173 			data->cy = py - offset;
3174 		}
3175 		data->oy = gd->hsize - offset;
3176 	}
3177 
3178 	if (!no_redraw && data->searchmark != NULL && !data->timeout)
3179 		window_copy_search_marks(wme, NULL, data->searchregex, 1);
3180 	window_copy_update_selection(wme, 1, 0);
3181 	if (!no_redraw)
3182 		window_copy_redraw_screen(wme);
3183 }
3184 
3185 static int
window_copy_search_compare(struct grid * gd,u_int px,u_int py,struct grid * sgd,u_int spx,int cis)3186 window_copy_search_compare(struct grid *gd, u_int px, u_int py,
3187     struct grid *sgd, u_int spx, int cis)
3188 {
3189 	struct grid_cell	 gc, sgc;
3190 	const struct utf8_data	*ud, *sud;
3191 
3192 	grid_get_cell(gd, px, py, &gc);
3193 	ud = &gc.data;
3194 	grid_get_cell(sgd, spx, 0, &sgc);
3195 	sud = &sgc.data;
3196 
3197 	if (*sud->data == '\t' && sud->size == 1 && gc.flags & GRID_FLAG_TAB)
3198 		return (1);
3199 
3200 	if (ud->size != sud->size || ud->width != sud->width)
3201 		return (0);
3202 
3203 	if (cis && ud->size == 1)
3204 		return (tolower(ud->data[0]) == sud->data[0]);
3205 
3206 	return (memcmp(ud->data, sud->data, ud->size) == 0);
3207 }
3208 
3209 static int
window_copy_search_lr(struct grid * gd,struct grid * sgd,u_int * ppx,u_int py,u_int first,u_int last,int cis)3210 window_copy_search_lr(struct grid *gd, struct grid *sgd, u_int *ppx, u_int py,
3211     u_int first, u_int last, int cis)
3212 {
3213 	u_int			 ax, bx, px, pywrap, endline, padding;
3214 	int			 matched;
3215 	struct grid_line	*gl;
3216 	struct grid_cell	 gc;
3217 
3218 	endline = gd->hsize + gd->sy - 1;
3219 	for (ax = first; ax < last; ax++) {
3220 		padding = 0;
3221 		for (bx = 0; bx < sgd->sx; bx++) {
3222 			px = ax + bx + padding;
3223 			pywrap = py;
3224 			/* Wrap line. */
3225 			while (px >= gd->sx && pywrap < endline) {
3226 				gl = grid_get_line(gd, pywrap);
3227 				if (~gl->flags & GRID_LINE_WRAPPED)
3228 					break;
3229 				px -= gd->sx;
3230 				pywrap++;
3231 			}
3232 			/* We have run off the end of the grid. */
3233 			if (px - padding >= gd->sx)
3234 				break;
3235 
3236 			grid_get_cell(gd, px, pywrap, &gc);
3237 			if (gc.flags & GRID_FLAG_TAB)
3238 				padding += gc.data.width - 1;
3239 
3240 			matched = window_copy_search_compare(gd, px, pywrap,
3241 			    sgd, bx, cis);
3242 			if (!matched)
3243 				break;
3244 		}
3245 		if (bx == sgd->sx) {
3246 			*ppx = ax;
3247 			return (1);
3248 		}
3249 	}
3250 	return (0);
3251 }
3252 
3253 static int
window_copy_search_rl(struct grid * gd,struct grid * sgd,u_int * ppx,u_int py,u_int first,u_int last,int cis)3254 window_copy_search_rl(struct grid *gd,
3255     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
3256 {
3257 	u_int			 ax, bx, px, pywrap, endline, padding;
3258 	int			 matched;
3259 	struct grid_line	*gl;
3260 	struct grid_cell	 gc;
3261 
3262 	endline = gd->hsize + gd->sy - 1;
3263 	for (ax = last; ax > first; ax--) {
3264 		padding = 0;
3265 		for (bx = 0; bx < sgd->sx; bx++) {
3266 			px = ax - 1 + bx + padding;
3267 			pywrap = py;
3268 			/* Wrap line. */
3269 			while (px >= gd->sx && pywrap < endline) {
3270 				gl = grid_get_line(gd, pywrap);
3271 				if (~gl->flags & GRID_LINE_WRAPPED)
3272 					break;
3273 				px -= gd->sx;
3274 				pywrap++;
3275 			}
3276 			/* We have run off the end of the grid. */
3277 			if (px - padding >= gd->sx)
3278 				break;
3279 
3280 			grid_get_cell(gd, px, pywrap, &gc);
3281 			if (gc.flags & GRID_FLAG_TAB)
3282 				padding += gc.data.width - 1;
3283 
3284 			matched = window_copy_search_compare(gd, px, pywrap,
3285 			    sgd, bx, cis);
3286 			if (!matched)
3287 				break;
3288 		}
3289 		if (bx == sgd->sx) {
3290 			*ppx = ax - 1;
3291 			return (1);
3292 		}
3293 	}
3294 	return (0);
3295 }
3296 
3297 static int
window_copy_search_lr_regex(struct grid * gd,u_int * ppx,u_int * psx,u_int py,u_int first,u_int last,regex_t * reg)3298 window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3299     u_int first, u_int last, regex_t *reg)
3300 {
3301 	int			eflags = 0;
3302 	u_int			endline, foundx, foundy, len, pywrap, size = 1;
3303 	char		       *buf;
3304 	regmatch_t		regmatch;
3305 	struct grid_line       *gl;
3306 
3307 	/*
3308 	 * This can happen during search if the last match was the last
3309 	 * character on a line.
3310 	 */
3311 	if (first >= last)
3312 		return (0);
3313 
3314 	/* Set flags for regex search. */
3315 	if (first != 0)
3316 		eflags |= REG_NOTBOL;
3317 
3318 	/* Need to look at the entire string. */
3319 	buf = xmalloc(size);
3320 	buf[0] = '\0';
3321 	buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3322 	len = gd->sx - first;
3323 	endline = gd->hsize + gd->sy - 1;
3324 	pywrap = py;
3325 	while (buf != NULL &&
3326 	    pywrap <= endline &&
3327 	    len < WINDOW_COPY_SEARCH_MAX_LINE) {
3328 		gl = grid_get_line(gd, pywrap);
3329 		if (~gl->flags & GRID_LINE_WRAPPED)
3330 			break;
3331 		pywrap++;
3332 		buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3333 		len += gd->sx;
3334 	}
3335 
3336 	if (regexec(reg, buf, 1, &regmatch, eflags) == 0 &&
3337 	    regmatch.rm_so != regmatch.rm_eo) {
3338 		foundx = first;
3339 		foundy = py;
3340 		window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3341 		    buf + regmatch.rm_so);
3342 		if (foundy == py && foundx < last) {
3343 			*ppx = foundx;
3344 			len -= foundx - first;
3345 			window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3346 			    buf + regmatch.rm_eo);
3347 			*psx = foundx;
3348 			while (foundy > py) {
3349 				*psx += gd->sx;
3350 				foundy--;
3351 			}
3352 			*psx -= *ppx;
3353 			free(buf);
3354 			return (1);
3355 		}
3356 	}
3357 
3358 	free(buf);
3359 	*ppx = 0;
3360 	*psx = 0;
3361 	return (0);
3362 }
3363 
3364 static int
window_copy_search_rl_regex(struct grid * gd,u_int * ppx,u_int * psx,u_int py,u_int first,u_int last,regex_t * reg)3365 window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
3366     u_int first, u_int last, regex_t *reg)
3367 {
3368 	int			eflags = 0;
3369 	u_int			endline, len, pywrap, size = 1;
3370 	char		       *buf;
3371 	struct grid_line       *gl;
3372 
3373 	/* Set flags for regex search. */
3374 	if (first != 0)
3375 		eflags |= REG_NOTBOL;
3376 
3377 	/* Need to look at the entire string. */
3378 	buf = xmalloc(size);
3379 	buf[0] = '\0';
3380 	buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
3381 	len = gd->sx - first;
3382 	endline = gd->hsize + gd->sy - 1;
3383 	pywrap = py;
3384 	while (buf != NULL &&
3385 	    pywrap <= endline &&
3386 	    len < WINDOW_COPY_SEARCH_MAX_LINE) {
3387 		gl = grid_get_line(gd, pywrap);
3388 		if (~gl->flags & GRID_LINE_WRAPPED)
3389 			break;
3390 		pywrap++;
3391 		buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
3392 		len += gd->sx;
3393 	}
3394 
3395 	if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf,
3396 	    reg, eflags))
3397 	{
3398 		free(buf);
3399 		return (1);
3400 	}
3401 
3402 	free(buf);
3403 	*ppx = 0;
3404 	*psx = 0;
3405 	return (0);
3406 }
3407 
3408 static const char *
window_copy_cellstring(const struct grid_line * gl,u_int px,size_t * size,int * allocated)3409 window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size,
3410     int *allocated)
3411 {
3412 	static struct utf8_data	 ud;
3413 	struct grid_cell_entry	*gce;
3414 	char			*copy;
3415 
3416 	if (px >= gl->cellsize) {
3417 		*size = 1;
3418 		*allocated = 0;
3419 		return (" ");
3420 	}
3421 
3422 	gce = &gl->celldata[px];
3423 	if (gce->flags & GRID_FLAG_PADDING) {
3424 		*size = 0;
3425 		*allocated = 0;
3426 		return (NULL);
3427 	}
3428 	if (~gce->flags & GRID_FLAG_EXTENDED) {
3429 		*size = 1;
3430 		*allocated = 0;
3431 		return (&gce->data.data);
3432 	}
3433 	if (gce->flags & GRID_FLAG_TAB) {
3434 		*size = 1;
3435 		*allocated = 0;
3436 		return ("\t");
3437 	}
3438 
3439 	utf8_to_data(gl->extddata[gce->offset].data, &ud);
3440 	if (ud.size == 0) {
3441 		*size = 0;
3442 		*allocated = 0;
3443 		return (NULL);
3444 	}
3445 	*size = ud.size;
3446 	*allocated = 1;
3447 
3448 	copy = xmalloc(ud.size);
3449 	memcpy(copy, ud.data, ud.size);
3450 	return (copy);
3451 }
3452 
3453 /* Find last match in given range. */
3454 static int
window_copy_last_regex(struct grid * gd,u_int py,u_int first,u_int last,u_int len,u_int * ppx,u_int * psx,const char * buf,const regex_t * preg,int eflags)3455 window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last,
3456     u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg,
3457     int eflags)
3458 {
3459 	u_int		foundx, foundy, oldx, px = 0, savepx, savesx = 0;
3460 	regmatch_t	regmatch;
3461 
3462 	foundx = first;
3463 	foundy = py;
3464 	oldx = first;
3465 	while (regexec(preg, buf + px, 1, &regmatch, eflags) == 0) {
3466 		if (regmatch.rm_so == regmatch.rm_eo)
3467 			break;
3468 		window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3469 		    buf + px + regmatch.rm_so);
3470 		if (foundy > py || foundx >= last)
3471 			break;
3472 		len -= foundx - oldx;
3473 		savepx = foundx;
3474 		window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
3475 		    buf + px + regmatch.rm_eo);
3476 		if (foundy > py || foundx >= last) {
3477 			*ppx = savepx;
3478 			*psx = foundx;
3479 			while (foundy > py) {
3480 				*psx += gd->sx;
3481 				foundy--;
3482 			}
3483 			*psx -= *ppx;
3484 			return (1);
3485 		} else {
3486 			savesx = foundx - savepx;
3487 			len -= savesx;
3488 			oldx = foundx;
3489 		}
3490 		px += regmatch.rm_eo;
3491 	}
3492 
3493 	if (savesx > 0) {
3494 		*ppx = savepx;
3495 		*psx = savesx;
3496 		return (1);
3497 	} else {
3498 		*ppx = 0;
3499 		*psx = 0;
3500 		return (0);
3501 	}
3502 }
3503 
3504 /* Stringify line and append to input buffer. Caller frees. */
3505 static char *
window_copy_stringify(struct grid * gd,u_int py,u_int first,u_int last,char * buf,u_int * size)3506 window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
3507     char *buf, u_int *size)
3508 {
3509 	u_int			 ax, bx, newsize = *size;
3510 	const struct grid_line	*gl;
3511 	const char		*d;
3512 	size_t			 bufsize = 1024, dlen;
3513 	int			 allocated;
3514 
3515 	while (bufsize < newsize)
3516 		bufsize *= 2;
3517 	buf = xrealloc(buf, bufsize);
3518 
3519 	gl = grid_peek_line(gd, py);
3520 	bx = *size - 1;
3521 	for (ax = first; ax < last; ax++) {
3522 		d = window_copy_cellstring(gl, ax, &dlen, &allocated);
3523 		newsize += dlen;
3524 		while (bufsize < newsize) {
3525 			bufsize *= 2;
3526 			buf = xrealloc(buf, bufsize);
3527 		}
3528 		if (dlen == 1)
3529 			buf[bx++] = *d;
3530 		else {
3531 			memcpy(buf + bx, d, dlen);
3532 			bx += dlen;
3533 		}
3534 		if (allocated)
3535 			free((void *)d);
3536 	}
3537 	buf[newsize - 1] = '\0';
3538 
3539 	*size = newsize;
3540 	return (buf);
3541 }
3542 
3543 /* Map start of C string containing UTF-8 data to grid cell position. */
3544 static void
window_copy_cstrtocellpos(struct grid * gd,u_int ncells,u_int * ppx,u_int * ppy,const char * str)3545 window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
3546     const char *str)
3547 {
3548 	u_int			 cell, ccell, px, pywrap, pos, len;
3549 	int			 match;
3550 	const struct grid_line	*gl;
3551 	const char		*d;
3552 	size_t			 dlen;
3553 	struct {
3554 		const char	*d;
3555 		size_t		 dlen;
3556 		int		 allocated;
3557 	} *cells;
3558 
3559 	/* Populate the array of cell data. */
3560 	cells = xreallocarray(NULL, ncells, sizeof cells[0]);
3561 	cell = 0;
3562 	px = *ppx;
3563 	pywrap = *ppy;
3564 	gl = grid_peek_line(gd, pywrap);
3565 	while (cell < ncells) {
3566 		cells[cell].d = window_copy_cellstring(gl, px,
3567 		    &cells[cell].dlen, &cells[cell].allocated);
3568 		cell++;
3569 		px++;
3570 		if (px == gd->sx) {
3571 			px = 0;
3572 			pywrap++;
3573 			gl = grid_peek_line(gd, pywrap);
3574 		}
3575 	}
3576 
3577 	/* Locate starting cell. */
3578 	cell = 0;
3579 	len = strlen(str);
3580 	while (cell < ncells) {
3581 		ccell = cell;
3582 		pos = 0;
3583 		match = 1;
3584 		while (ccell < ncells) {
3585 			if (str[pos] == '\0') {
3586 				match = 0;
3587 				break;
3588 			}
3589 			d = cells[ccell].d;
3590 			dlen = cells[ccell].dlen;
3591 			if (dlen == 1) {
3592 				if (str[pos] != *d) {
3593 					match = 0;
3594 					break;
3595 				}
3596 				pos++;
3597 			} else {
3598 				if (dlen > len - pos)
3599 					dlen = len - pos;
3600 				if (memcmp(str + pos, d, dlen) != 0) {
3601 					match = 0;
3602 					break;
3603 				}
3604 				pos += dlen;
3605 			}
3606 			ccell++;
3607 		}
3608 		if (match)
3609 			break;
3610 		cell++;
3611 	}
3612 
3613 	/* If not found this will be one past the end. */
3614 	px = *ppx + cell;
3615 	pywrap = *ppy;
3616 	while (px >= gd->sx) {
3617 		px -= gd->sx;
3618 		pywrap++;
3619 	}
3620 
3621 	*ppx = px;
3622 	*ppy = pywrap;
3623 
3624 	/* Free cell data. */
3625 	for (cell = 0; cell < ncells; cell++) {
3626 		if (cells[cell].allocated)
3627 			free((void *)cells[cell].d);
3628 	}
3629 	free(cells);
3630 }
3631 
3632 static void
window_copy_move_left(struct screen * s,u_int * fx,u_int * fy,int wrapflag)3633 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3634 {
3635 	if (*fx == 0) {	/* left */
3636 		if (*fy == 0) { /* top */
3637 			if (wrapflag) {
3638 				*fx = screen_size_x(s) - 1;
3639 				*fy = screen_hsize(s) + screen_size_y(s) - 1;
3640 			}
3641 			return;
3642 		}
3643 		*fx = screen_size_x(s) - 1;
3644 		*fy = *fy - 1;
3645 	} else
3646 		*fx = *fx - 1;
3647 }
3648 
3649 static void
window_copy_move_right(struct screen * s,u_int * fx,u_int * fy,int wrapflag)3650 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
3651 {
3652 	if (*fx == screen_size_x(s) - 1) { /* right */
3653 		if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
3654 			if (wrapflag) {
3655 				*fx = 0;
3656 				*fy = 0;
3657 			}
3658 			return;
3659 		}
3660 		*fx = 0;
3661 		*fy = *fy + 1;
3662 	} else
3663 		*fx = *fx + 1;
3664 }
3665 
3666 static int
window_copy_is_lowercase(const char * ptr)3667 window_copy_is_lowercase(const char *ptr)
3668 {
3669 	while (*ptr != '\0') {
3670 		if (*ptr != tolower((u_char)*ptr))
3671 			return (0);
3672 		++ptr;
3673 	}
3674 	return (1);
3675 }
3676 
3677 /*
3678  * Handle backward wrapped regex searches with overlapping matches. In this case
3679  * find the longest overlapping match from previous wrapped lines.
3680  */
3681 static void
window_copy_search_back_overlap(struct grid * gd,regex_t * preg,u_int * ppx,u_int * psx,u_int * ppy,u_int endline)3682 window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx,
3683     u_int *psx, u_int *ppy, u_int endline)
3684 {
3685 	u_int	endx, endy, oldendx, oldendy, px, py, sx;
3686 	int	found = 1;
3687 
3688 	oldendx = *ppx + *psx;
3689 	oldendy = *ppy - 1;
3690 	while (oldendx > gd->sx - 1) {
3691 		oldendx -= gd->sx;
3692 		oldendy++;
3693 	}
3694 	endx = oldendx;
3695 	endy = oldendy;
3696 	px = *ppx;
3697 	py = *ppy;
3698 	while (found && px == 0 && py - 1 > endline &&
3699 	       grid_get_line(gd, py - 2)->flags & GRID_LINE_WRAPPED &&
3700 	       endx == oldendx && endy == oldendy) {
3701 		py--;
3702 		found = window_copy_search_rl_regex(gd, &px, &sx, py - 1, 0,
3703 		    gd->sx, preg);
3704 		if (found) {
3705 			endx = px + sx;
3706 			endy = py - 1;
3707 			while (endx > gd->sx - 1) {
3708 				endx -= gd->sx;
3709 				endy++;
3710 			}
3711 			if (endx == oldendx && endy == oldendy) {
3712 				*ppx = px;
3713 				*ppy = py;
3714 			}
3715 		}
3716 	}
3717 }
3718 
3719 /*
3720  * Search for text stored in sgd starting from position fx,fy up to endline. If
3721  * found, jump to it. If cis then ignore case. The direction is 0 for searching
3722  * up, down otherwise. If wrap then go to begin/end of grid and try again if
3723  * not found.
3724  */
3725 static int
window_copy_search_jump(struct window_mode_entry * wme,struct grid * gd,struct grid * sgd,u_int fx,u_int fy,u_int endline,int cis,int wrap,int direction,int regex)3726 window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
3727     struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
3728     int direction, int regex)
3729 {
3730 	u_int	 i, px, sx, ssize = 1;
3731 	int	 found = 0, cflags = REG_EXTENDED;
3732 	char	*sbuf;
3733 	regex_t	 reg;
3734 
3735 	if (regex) {
3736 		sbuf = xmalloc(ssize);
3737 		sbuf[0] = '\0';
3738 		sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize);
3739 		if (cis)
3740 			cflags |= REG_ICASE;
3741 		if (regcomp(&reg, sbuf, cflags) != 0) {
3742 			free(sbuf);
3743 			return (0);
3744 		}
3745 		free(sbuf);
3746 	}
3747 
3748 	if (direction) {
3749 		for (i = fy; i <= endline; i++) {
3750 			if (regex) {
3751 				found = window_copy_search_lr_regex(gd,
3752 				    &px, &sx, i, fx, gd->sx, &reg);
3753 			} else {
3754 				found = window_copy_search_lr(gd, sgd,
3755 				    &px, i, fx, gd->sx, cis);
3756 			}
3757 			if (found)
3758 				break;
3759 			fx = 0;
3760 		}
3761 	} else {
3762 		for (i = fy + 1; endline < i; i--) {
3763 			if (regex) {
3764 				found = window_copy_search_rl_regex(gd,
3765 				    &px, &sx, i - 1, 0, fx + 1, &reg);
3766 				if (found) {
3767 					window_copy_search_back_overlap(gd,
3768 					    &reg, &px, &sx, &i, endline);
3769 				}
3770 			} else {
3771 				found = window_copy_search_rl(gd, sgd,
3772 				    &px, i - 1, 0, fx + 1, cis);
3773 			}
3774 			if (found) {
3775 				i--;
3776 				break;
3777 			}
3778 			fx = gd->sx - 1;
3779 		}
3780 	}
3781 	if (regex)
3782 		regfree(&reg);
3783 
3784 	if (found) {
3785 		window_copy_scroll_to(wme, px, i, 1);
3786 		return (1);
3787 	}
3788 	if (wrap) {
3789 		return (window_copy_search_jump(wme, gd, sgd,
3790 		    direction ? 0 : gd->sx - 1,
3791 		    direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
3792 		    direction, regex));
3793 	}
3794 	return (0);
3795 }
3796 
3797 static void
window_copy_move_after_search_mark(struct window_copy_mode_data * data,u_int * fx,u_int * fy,int wrapflag)3798 window_copy_move_after_search_mark(struct window_copy_mode_data *data,
3799     u_int *fx, u_int *fy, int wrapflag)
3800 {
3801 	struct screen  *s = data->backing;
3802 	u_int		at, start;
3803 
3804 	if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 &&
3805 	    data->searchmark[start] != 0) {
3806 		while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) {
3807 			if (data->searchmark[at] != data->searchmark[start])
3808 				break;
3809 			/* Stop if not wrapping and at the end of the grid. */
3810 			if (!wrapflag &&
3811 			    *fx == screen_size_x(s) - 1 &&
3812 			    *fy == screen_hsize(s) + screen_size_y(s) - 1)
3813 				break;
3814 
3815 			window_copy_move_right(s, fx, fy, wrapflag);
3816 		}
3817 	}
3818 }
3819 
3820 /*
3821  * Search in for text searchstr. If direction is 0 then search up, otherwise
3822  * down.
3823  */
3824 static int
window_copy_search(struct window_mode_entry * wme,int direction,int regex)3825 window_copy_search(struct window_mode_entry *wme, int direction, int regex)
3826 {
3827 	struct window_pane		*wp = wme->wp;
3828 	struct window_copy_mode_data	*data = wme->data;
3829 	struct screen			*s = data->backing, ss;
3830 	struct screen_write_ctx		 ctx;
3831 	struct grid			*gd = s->grid;
3832 	const char			*str = data->searchstr;
3833 	u_int				 at, endline, fx, fy, start, ssx;
3834 	int				 cis, found, keys, visible_only;
3835 	int				 wrapflag;
3836 
3837 	if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
3838 		regex = 0;
3839 
3840 	data->searchdirection = direction;
3841 
3842 	if (data->timeout)
3843 		return (0);
3844 
3845 	if (data->searchall || wp->searchstr == NULL ||
3846 	    wp->searchregex != regex) {
3847 		visible_only = 0;
3848 		data->searchall = 0;
3849 	} else
3850 		visible_only = (strcmp(wp->searchstr, str) == 0);
3851 	if (visible_only == 0 && data->searchmark != NULL)
3852 		window_copy_clear_marks(wme);
3853 	free(wp->searchstr);
3854 	wp->searchstr = xstrdup(str);
3855 	wp->searchregex = regex;
3856 
3857 	fx = data->cx;
3858 	fy = screen_hsize(data->backing) - data->oy + data->cy;
3859 
3860 	if ((ssx = screen_write_strlen("%s", str)) == 0)
3861 		return (0);
3862 	screen_init(&ss, ssx, 1, 0);
3863 	screen_write_start(&ctx, &ss);
3864 	screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str);
3865 	screen_write_stop(&ctx);
3866 
3867 	wrapflag = options_get_number(wp->window->options, "wrap-search");
3868 	cis = window_copy_is_lowercase(str);
3869 
3870 	keys = options_get_number(wp->window->options, "mode-keys");
3871 
3872 	if (direction) {
3873 		/*
3874 		 * Behave according to mode-keys. If it is emacs, search forward
3875 		 * leaves the cursor after the match. If it is vi, the cursor
3876 		 * remains at the beginning of the match, regardless of
3877 		 * direction, which means that we need to start the next search
3878 		 * after the term the cursor is currently on when searching
3879 		 * forward.
3880 		 */
3881 		if (keys == MODEKEY_VI) {
3882 			if (data->searchmark != NULL)
3883 				window_copy_move_after_search_mark(data, &fx,
3884 				    &fy, wrapflag);
3885 			else {
3886 				/*
3887 				 * When there are no search marks, start the
3888 				 * search after the current cursor position.
3889 				 */
3890 				window_copy_move_right(s, &fx, &fy, wrapflag);
3891 			}
3892 		}
3893 		endline = gd->hsize + gd->sy - 1;
3894 	} else {
3895 		window_copy_move_left(s, &fx, &fy, wrapflag);
3896 		endline = 0;
3897 	}
3898 
3899 	found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
3900 	    wrapflag, direction, regex);
3901 	if (found) {
3902 		window_copy_search_marks(wme, &ss, regex, visible_only);
3903 		fx = data->cx;
3904 		fy = screen_hsize(data->backing) - data->oy + data->cy;
3905 
3906 		/*
3907 		 * When searching forward, if the cursor is not at the beginning
3908 		 * of the mark, search again.
3909 		 */
3910 		if (direction &&
3911 		    window_copy_search_mark_at(data, fx, fy, &at) == 0 &&
3912 		    at > 0 &&
3913 		    data->searchmark != NULL &&
3914 		    data->searchmark[at] == data->searchmark[at - 1]) {
3915 			window_copy_move_after_search_mark(data, &fx, &fy,
3916 			    wrapflag);
3917 			window_copy_search_jump(wme, gd, ss.grid, fx,
3918 			    fy, endline, cis, wrapflag, direction,
3919 			    regex);
3920 			fx = data->cx;
3921 			fy = screen_hsize(data->backing) - data->oy + data->cy;
3922 		}
3923 
3924 		if (direction) {
3925 			/*
3926 			 * When in Emacs mode, position the cursor just after
3927 			 * the mark.
3928 			 */
3929 			if (keys == MODEKEY_EMACS) {
3930 				window_copy_move_after_search_mark(data, &fx,
3931 				    &fy, wrapflag);
3932 				data->cx = fx;
3933 				data->cy = fy - screen_hsize(data->backing) +
3934 				    data-> oy;
3935 			}
3936 		} else {
3937 			/*
3938 			 * When searching backward, position the cursor at the
3939 			 * beginning of the mark.
3940 			 */
3941 			if (window_copy_search_mark_at(data, fx, fy,
3942 			        &start) == 0) {
3943 				while (window_copy_search_mark_at(data, fx, fy,
3944 				           &at) == 0 &&
3945 				       data->searchmark != NULL &&
3946 				       data->searchmark[at] ==
3947 				           data->searchmark[start]) {
3948 					data->cx = fx;
3949 					data->cy = fy -
3950 					    screen_hsize(data->backing) +
3951 					    data-> oy;
3952 					if (at == 0)
3953 						break;
3954 
3955 					window_copy_move_left(s, &fx, &fy, 0);
3956 				}
3957 			}
3958 		}
3959 	}
3960 	window_copy_redraw_screen(wme);
3961 
3962 	screen_free(&ss);
3963 	return (found);
3964 }
3965 
3966 static void
window_copy_visible_lines(struct window_copy_mode_data * data,u_int * start,u_int * end)3967 window_copy_visible_lines(struct window_copy_mode_data *data, u_int *start,
3968     u_int *end)
3969 {
3970 	struct grid		*gd = data->backing->grid;
3971 	const struct grid_line	*gl;
3972 
3973 	for (*start = gd->hsize - data->oy; *start > 0; (*start)--) {
3974 		gl = grid_peek_line(gd, (*start) - 1);
3975 		if (~gl->flags & GRID_LINE_WRAPPED)
3976 			break;
3977 	}
3978 	*end = gd->hsize - data->oy + gd->sy;
3979 }
3980 
3981 static int
window_copy_search_mark_at(struct window_copy_mode_data * data,u_int px,u_int py,u_int * at)3982 window_copy_search_mark_at(struct window_copy_mode_data *data, u_int px,
3983     u_int py, u_int *at)
3984 {
3985 	struct screen	*s = data->backing;
3986 	struct grid	*gd = s->grid;
3987 
3988 	if (py < gd->hsize - data->oy)
3989 		return (-1);
3990 	if (py > gd->hsize - data->oy + gd->sy - 1)
3991 		return (-1);
3992 	*at = ((py - (gd->hsize - data->oy)) * gd->sx) + px;
3993 	return (0);
3994 }
3995 
3996 static u_int
window_copy_clip_width(u_int width,u_int b,u_int sx,u_int sy)3997 window_copy_clip_width(u_int width, u_int b, u_int sx, u_int sy)
3998 {
3999 	return ((b + width > sx * sy) ? (sx * sy) - b : width);
4000 }
4001 
4002 static u_int
window_copy_search_mark_match(struct window_copy_mode_data * data,u_int px,u_int py,u_int width,int regex)4003 window_copy_search_mark_match(struct window_copy_mode_data *data, u_int px,
4004     u_int py, u_int width, int regex)
4005 {
4006 	struct grid		*gd = data->backing->grid;
4007 	struct grid_cell	 gc;
4008 	u_int			 i, b, w = width, sx = gd->sx, sy = gd->sy;
4009 
4010 	if (window_copy_search_mark_at(data, px, py, &b) == 0) {
4011 		width = window_copy_clip_width(width, b, sx, sy);
4012 		w = width;
4013 		for (i = b; i < b + w; i++) {
4014 			if (!regex) {
4015 				grid_get_cell(gd, px + (i - b), py, &gc);
4016 				if (gc.flags & GRID_FLAG_TAB)
4017 					w += gc.data.width - 1;
4018 				w = window_copy_clip_width(w, b, sx, sy);
4019 			}
4020 			if (data->searchmark[i] != 0)
4021 				continue;
4022 			data->searchmark[i] = data->searchgen;
4023 		}
4024 		if (data->searchgen == UCHAR_MAX)
4025 			data->searchgen = 1;
4026 		else
4027 			data->searchgen++;
4028 	}
4029 
4030 	return (w);
4031 }
4032 
4033 static int
window_copy_search_marks(struct window_mode_entry * wme,struct screen * ssp,int regex,int visible_only)4034 window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
4035     int regex, int visible_only)
4036 {
4037 	struct window_copy_mode_data	*data = wme->data;
4038 	struct screen			*s = data->backing, ss;
4039 	struct screen_write_ctx		 ctx;
4040 	struct grid			*gd = s->grid;
4041 	struct grid_cell		 gc;
4042 	int				 found, cis, stopped = 0;
4043 	int				 cflags = REG_EXTENDED;
4044 	u_int				 px, py, nfound = 0, width;
4045 	u_int				 ssize = 1, start, end, sx = gd->sx;
4046 	u_int				 sy = gd->sy;
4047 	char				*sbuf;
4048 	regex_t				 reg;
4049 	uint64_t			 stop = 0, tstart, t;
4050 
4051 	if (ssp == NULL) {
4052 		width = screen_write_strlen("%s", data->searchstr);
4053 		screen_init(&ss, width, 1, 0);
4054 		screen_write_start(&ctx, &ss);
4055 		screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
4056 		    data->searchstr);
4057 		screen_write_stop(&ctx);
4058 		ssp = &ss;
4059 	} else
4060 		width = screen_size_x(ssp);
4061 
4062 	cis = window_copy_is_lowercase(data->searchstr);
4063 
4064 	if (regex) {
4065 		sbuf = xmalloc(ssize);
4066 		sbuf[0] = '\0';
4067 		sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx,
4068 		    sbuf, &ssize);
4069 		if (cis)
4070 			cflags |= REG_ICASE;
4071 		if (regcomp(&reg, sbuf, cflags) != 0) {
4072 			free(sbuf);
4073 			return (0);
4074 		}
4075 		free(sbuf);
4076 	}
4077 	tstart = get_timer();
4078 
4079 	if (visible_only)
4080 		window_copy_visible_lines(data, &start, &end);
4081 	else {
4082 		start = 0;
4083 		end = gd->hsize + sy;
4084 		stop = get_timer() + WINDOW_COPY_SEARCH_ALL_TIMEOUT;
4085 	}
4086 
4087 again:
4088 	free(data->searchmark);
4089 	data->searchmark = xcalloc(sx, sy);
4090 	data->searchgen = 1;
4091 
4092 	for (py = start; py < end; py++) {
4093 		px = 0;
4094 		for (;;) {
4095 			if (regex) {
4096 				found = window_copy_search_lr_regex(gd,
4097 				    &px, &width, py, px, sx, &reg);
4098 				grid_get_cell(gd, px + width - 1, py, &gc);
4099 				if (gc.data.width > 2)
4100 					width += gc.data.width - 1;
4101 				if (!found)
4102 					break;
4103 			} else {
4104 				found = window_copy_search_lr(gd, ssp->grid,
4105 				    &px, py, px, sx, cis);
4106 				if (!found)
4107 					break;
4108 			}
4109 			nfound++;
4110 			px += window_copy_search_mark_match(data, px, py, width,
4111 			    regex);
4112 		}
4113 
4114 		t = get_timer();
4115 		if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) {
4116 			data->timeout = 1;
4117 			break;
4118 		}
4119 		if (stop != 0 && t > stop) {
4120 			stopped = 1;
4121 			break;
4122 		}
4123 	}
4124 	if (data->timeout) {
4125 		window_copy_clear_marks(wme);
4126 		goto out;
4127 	}
4128 
4129 	if (stopped && stop != 0) {
4130 		/* Try again but just the visible context. */
4131 		window_copy_visible_lines(data, &start, &end);
4132 		stop = 0;
4133 		goto again;
4134 	}
4135 
4136 	if (!visible_only) {
4137 		if (stopped) {
4138 			if (nfound > 1000)
4139 				data->searchcount = 1000;
4140 			else if (nfound > 100)
4141 				data->searchcount = 100;
4142 			else if (nfound > 10)
4143 				data->searchcount = 10;
4144 			else
4145 				data->searchcount = -1;
4146 			data->searchmore = 1;
4147 		} else {
4148 			data->searchcount = nfound;
4149 			data->searchmore = 0;
4150 		}
4151 	}
4152 
4153 out:
4154 	if (ssp == &ss)
4155 		screen_free(&ss);
4156 	if (regex)
4157 		regfree(&reg);
4158 	return (1);
4159 }
4160 
4161 static void
window_copy_clear_marks(struct window_mode_entry * wme)4162 window_copy_clear_marks(struct window_mode_entry *wme)
4163 {
4164 	struct window_copy_mode_data	*data = wme->data;
4165 
4166 	free(data->searchmark);
4167 	data->searchmark = NULL;
4168 }
4169 
4170 static int
window_copy_search_up(struct window_mode_entry * wme,int regex)4171 window_copy_search_up(struct window_mode_entry *wme, int regex)
4172 {
4173 	return (window_copy_search(wme, 0, regex));
4174 }
4175 
4176 static int
window_copy_search_down(struct window_mode_entry * wme,int regex)4177 window_copy_search_down(struct window_mode_entry *wme, int regex)
4178 {
4179 	return (window_copy_search(wme, 1, regex));
4180 }
4181 
4182 static void
window_copy_goto_line(struct window_mode_entry * wme,const char * linestr)4183 window_copy_goto_line(struct window_mode_entry *wme, const char *linestr)
4184 {
4185 	struct window_copy_mode_data	*data = wme->data;
4186 	const char			*errstr;
4187 	int				 lineno;
4188 
4189 	lineno = strtonum(linestr, -1, INT_MAX, &errstr);
4190 	if (errstr != NULL)
4191 		return;
4192 	if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing))
4193 		lineno = screen_hsize(data->backing);
4194 
4195 	data->oy = lineno;
4196 	window_copy_update_selection(wme, 1, 0);
4197 	window_copy_redraw_screen(wme);
4198 }
4199 
4200 static void
window_copy_match_start_end(struct window_copy_mode_data * data,u_int at,u_int * start,u_int * end)4201 window_copy_match_start_end(struct window_copy_mode_data *data, u_int at,
4202     u_int *start, u_int *end)
4203 {
4204 	struct grid	*gd = data->backing->grid;
4205 	u_int		 last = (gd->sy * gd->sx) - 1;
4206 	u_char		 mark = data->searchmark[at];
4207 
4208 	*start = *end = at;
4209 	while (*start != 0 && data->searchmark[*start] == mark)
4210 		(*start)--;
4211 	if (data->searchmark[*start] != mark)
4212 		(*start)++;
4213 	while (*end != last && data->searchmark[*end] == mark)
4214 		(*end)++;
4215 	if (data->searchmark[*end] != mark)
4216 		(*end)--;
4217 }
4218 
4219 static char *
window_copy_match_at_cursor(struct window_copy_mode_data * data)4220 window_copy_match_at_cursor(struct window_copy_mode_data *data)
4221 {
4222 	struct grid	*gd = data->backing->grid;
4223 	struct grid_cell gc;
4224 	u_int		 at, start, end, cy, px, py;
4225 	u_int		 sx = screen_size_x(data->backing);
4226 	char		*buf = NULL;
4227 	size_t		 len = 0;
4228 
4229 	if (data->searchmark == NULL)
4230 		return (NULL);
4231 
4232 	cy = screen_hsize(data->backing) - data->oy + data->cy;
4233 	if (window_copy_search_mark_at(data, data->cx, cy, &at) != 0)
4234 		return (NULL);
4235 	if (data->searchmark[at] == 0) {
4236 		/* Allow one position after the match. */
4237 		if (at == 0 || data->searchmark[--at] == 0)
4238 			return (NULL);
4239 	}
4240 	window_copy_match_start_end(data, at, &start, &end);
4241 
4242 	/*
4243 	 * Cells will not be set in the marked array unless they are valid text
4244 	 * and wrapping will be taken care of, so we can just copy.
4245  	 */
4246 	for (at = start; at <= end; at++) {
4247 		py = at / sx;
4248 		px = at - (py * sx);
4249 
4250 		grid_get_cell(gd, px, gd->hsize + py - data->oy, &gc);
4251 		if (gc.flags & GRID_FLAG_TAB) {
4252 			buf = xrealloc(buf, len + 2);
4253 			buf[len] = '\t';
4254 			len++;
4255 		} else {
4256 			buf = xrealloc(buf, len + gc.data.size + 1);
4257 			memcpy(buf + len, gc.data.data, gc.data.size);
4258 			len += gc.data.size;
4259 		}
4260 	}
4261 	if (len != 0)
4262 		buf[len] = '\0';
4263 	return (buf);
4264 }
4265 
4266 static void
window_copy_update_style(struct window_mode_entry * wme,u_int fx,u_int fy,struct grid_cell * gc,const struct grid_cell * mgc,const struct grid_cell * cgc,const struct grid_cell * mkgc)4267 window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy,
4268     struct grid_cell *gc, const struct grid_cell *mgc,
4269     const struct grid_cell *cgc, const struct grid_cell *mkgc)
4270 {
4271 	struct window_pane		*wp = wme->wp;
4272 	struct window_copy_mode_data	*data = wme->data;
4273 	u_int				 mark, start, end, cy, cursor, current;
4274 	int				 inv = 0, found = 0;
4275 	int				 keys;
4276 
4277 	if (data->showmark && fy == data->my) {
4278 		gc->attr = mkgc->attr;
4279 		if (fx == data->mx)
4280 			inv = 1;
4281 		if (inv) {
4282 			gc->fg = mkgc->bg;
4283 			gc->bg = mkgc->fg;
4284 		}
4285 		else {
4286 			gc->fg = mkgc->fg;
4287 			gc->bg = mkgc->bg;
4288 		}
4289 	}
4290 
4291 	if (data->searchmark == NULL)
4292 		return;
4293 
4294 	if (window_copy_search_mark_at(data, fx, fy, &current) != 0)
4295 		return;
4296 	mark = data->searchmark[current];
4297 	if (mark == 0)
4298 		return;
4299 
4300 	cy = screen_hsize(data->backing) - data->oy + data->cy;
4301 	if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) {
4302 		keys = options_get_number(wp->window->options, "mode-keys");
4303 		if (cursor != 0 &&
4304 		    keys == MODEKEY_EMACS &&
4305 		    data->searchdirection) {
4306 			if (data->searchmark[cursor - 1] == mark) {
4307 				cursor--;
4308 				found = 1;
4309 			}
4310 		} else if (data->searchmark[cursor] == mark)
4311 			found = 1;
4312 		if (found) {
4313 			window_copy_match_start_end(data, cursor, &start, &end);
4314 			if (current >= start && current <= end) {
4315 				gc->attr = cgc->attr;
4316 				if (inv) {
4317 					gc->fg = cgc->bg;
4318 					gc->bg = cgc->fg;
4319 				}
4320 				else {
4321 					gc->fg = cgc->fg;
4322 					gc->bg = cgc->bg;
4323 				}
4324 				return;
4325 			}
4326 		}
4327 	}
4328 
4329 	gc->attr = mgc->attr;
4330 	if (inv) {
4331 		gc->fg = mgc->bg;
4332 		gc->bg = mgc->fg;
4333 	}
4334 	else {
4335 		gc->fg = mgc->fg;
4336 		gc->bg = mgc->bg;
4337 	}
4338 }
4339 
4340 static void
window_copy_write_one(struct window_mode_entry * wme,struct screen_write_ctx * ctx,u_int py,u_int fy,u_int nx,const struct grid_cell * mgc,const struct grid_cell * cgc,const struct grid_cell * mkgc)4341 window_copy_write_one(struct window_mode_entry *wme,
4342     struct screen_write_ctx *ctx, u_int py, u_int fy, u_int nx,
4343     const struct grid_cell *mgc, const struct grid_cell *cgc,
4344     const struct grid_cell *mkgc)
4345 {
4346 	struct window_copy_mode_data	*data = wme->data;
4347 	struct grid			*gd = data->backing->grid;
4348 	struct grid_cell		 gc;
4349 	u_int		 		 fx;
4350 
4351 	screen_write_cursormove(ctx, 0, py, 0);
4352 	for (fx = 0; fx < nx; fx++) {
4353 		grid_get_cell(gd, fx, fy, &gc);
4354 		if (fx + gc.data.width <= nx) {
4355 			window_copy_update_style(wme, fx, fy, &gc, mgc, cgc,
4356 			    mkgc);
4357 			screen_write_cell(ctx, &gc);
4358 		}
4359 	}
4360 }
4361 
4362 int
window_copy_get_current_offset(struct window_pane * wp,u_int * offset,u_int * size)4363 window_copy_get_current_offset(struct window_pane *wp, u_int *offset,
4364     u_int *size)
4365 {
4366 	struct window_mode_entry	*wme = TAILQ_FIRST(&wp->modes);
4367 	struct window_copy_mode_data	*data = wme->data;
4368 	u_int				 hsize;
4369 
4370 	if (data == NULL)
4371 		return (0);
4372 	hsize = screen_hsize(data->backing);
4373 
4374 	*offset = hsize - data->oy;
4375 	*size = hsize;
4376 	return (1);
4377 }
4378 
4379 static void
window_copy_write_line(struct window_mode_entry * wme,struct screen_write_ctx * ctx,u_int py)4380 window_copy_write_line(struct window_mode_entry *wme,
4381     struct screen_write_ctx *ctx, u_int py)
4382 {
4383 	struct window_pane		*wp = wme->wp;
4384 	struct window_copy_mode_data	*data = wme->data;
4385 	struct screen			*s = &data->screen;
4386 	struct options			*oo = wp->window->options;
4387 	struct grid_cell		 gc, mgc, cgc, mkgc;
4388 	u_int				 sx = screen_size_x(s);
4389 	u_int				 hsize = screen_hsize(data->backing);
4390 	const char			*value;
4391 	char				*expanded;
4392 	struct format_tree		*ft;
4393 
4394 	ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
4395 
4396 	style_apply(&gc, oo, "copy-mode-position-style", ft);
4397 	gc.flags |= GRID_FLAG_NOPALETTE;
4398 	style_apply(&mgc, oo, "copy-mode-match-style", ft);
4399 	mgc.flags |= GRID_FLAG_NOPALETTE;
4400 	style_apply(&cgc, oo, "copy-mode-current-match-style", ft);
4401 	cgc.flags |= GRID_FLAG_NOPALETTE;
4402 	style_apply(&mkgc, oo, "copy-mode-mark-style", ft);
4403 	mkgc.flags |= GRID_FLAG_NOPALETTE;
4404 
4405 	window_copy_write_one(wme, ctx, py, hsize - data->oy + py,
4406 	    screen_size_x(s), &mgc, &cgc, &mkgc);
4407 
4408 	if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
4409 		value = options_get_string(oo, "copy-mode-position-format");
4410 		if (*value != '\0') {
4411 			expanded = format_expand(ft, value);
4412 			if (*expanded != '\0') {
4413 				screen_write_cursormove(ctx, 0, 0, 0);
4414 				format_draw(ctx, &gc, sx, expanded, NULL, 0);
4415 			}
4416 			free(expanded);
4417 		}
4418 	}
4419 
4420 	if (py == data->cy && data->cx == screen_size_x(s)) {
4421 		screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0);
4422 		screen_write_putc(ctx, &grid_default_cell, '$');
4423 	}
4424 
4425 	format_free(ft);
4426 }
4427 
4428 static void
window_copy_write_lines(struct window_mode_entry * wme,struct screen_write_ctx * ctx,u_int py,u_int ny)4429 window_copy_write_lines(struct window_mode_entry *wme,
4430     struct screen_write_ctx *ctx, u_int py, u_int ny)
4431 {
4432 	u_int	yy;
4433 
4434 	for (yy = py; yy < py + ny; yy++)
4435 		window_copy_write_line(wme, ctx, py);
4436 }
4437 
4438 static void
window_copy_redraw_selection(struct window_mode_entry * wme,u_int old_y)4439 window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
4440 {
4441 	struct window_copy_mode_data	*data = wme->data;
4442 	struct grid			*gd = data->backing->grid;
4443 	u_int				 new_y, start, end;
4444 
4445 	new_y = data->cy;
4446 	if (old_y <= new_y) {
4447 		start = old_y;
4448 		end = new_y;
4449 	} else {
4450 		start = new_y;
4451 		end = old_y;
4452 	}
4453 
4454 	/*
4455 	 * In word selection mode the first word on the line below the cursor
4456 	 * might be selected, so add this line to the redraw area.
4457 	 */
4458 	if (data->selflag == SEL_WORD) {
4459 		/* Last grid line in data coordinates. */
4460 		if (end < gd->sy + data->oy - 1)
4461 			end++;
4462 	}
4463 	window_copy_redraw_lines(wme, start, end - start + 1);
4464 }
4465 
4466 static void
window_copy_redraw_lines(struct window_mode_entry * wme,u_int py,u_int ny)4467 window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
4468 {
4469 	struct window_pane		*wp = wme->wp;
4470 	struct window_copy_mode_data	*data = wme->data;
4471 	struct screen_write_ctx	 	 ctx;
4472 	u_int				 i;
4473 
4474 	screen_write_start_pane(&ctx, wp, NULL);
4475 	for (i = py; i < py + ny; i++)
4476 		window_copy_write_line(wme, &ctx, i);
4477 	screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4478 	screen_write_stop(&ctx);
4479 
4480 	wp->flags |= PANE_REDRAWSCROLLBAR;
4481 }
4482 
4483 static void
window_copy_redraw_screen(struct window_mode_entry * wme)4484 window_copy_redraw_screen(struct window_mode_entry *wme)
4485 {
4486 	struct window_copy_mode_data	*data = wme->data;
4487 
4488 	window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
4489 }
4490 
4491 static void
window_copy_synchronize_cursor_end(struct window_mode_entry * wme,int begin,int no_reset)4492 window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
4493     int no_reset)
4494 {
4495 	struct window_copy_mode_data	*data = wme->data;
4496 	u_int				 xx, yy;
4497 
4498 	xx = data->cx;
4499 	yy = screen_hsize(data->backing) + data->cy - data->oy;
4500 	switch (data->selflag) {
4501 	case SEL_WORD:
4502 		if (no_reset)
4503 			break;
4504 		begin = 0;
4505 		if (data->dy > yy || (data->dy == yy && data->dx > xx)) {
4506 			/* Right to left selection. */
4507 			window_copy_cursor_previous_word_pos(wme,
4508 			    data->separators, &xx, &yy);
4509 			begin = 1;
4510 
4511 			/* Reset the end. */
4512 			data->endselx = data->endselrx;
4513 			data->endsely = data->endselry;
4514 		} else {
4515 			/* Left to right selection. */
4516 			if (xx >= window_copy_find_length(wme, yy) ||
4517 			    !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) {
4518 				window_copy_cursor_next_word_end_pos(wme,
4519 				    data->separators, &xx, &yy);
4520 			}
4521 
4522 			/* Reset the start. */
4523 			data->selx = data->selrx;
4524 			data->sely = data->selry;
4525 		}
4526 		break;
4527 	case SEL_LINE:
4528 		if (no_reset)
4529 			break;
4530 		begin = 0;
4531 		if (data->dy > yy) {
4532 			/* Right to left selection. */
4533 			xx = 0;
4534 			begin = 1;
4535 
4536 			/* Reset the end. */
4537 			data->endselx = data->endselrx;
4538 			data->endsely = data->endselry;
4539 		} else {
4540 			/* Left to right selection. */
4541 			if (yy < data->endselry)
4542 				yy = data->endselry;
4543 			xx = window_copy_find_length(wme, yy);
4544 
4545 			/* Reset the start. */
4546 			data->selx = data->selrx;
4547 			data->sely = data->selry;
4548 		}
4549 		break;
4550 	case SEL_CHAR:
4551 		break;
4552 	}
4553 	if (begin) {
4554 		data->selx = xx;
4555 		data->sely = yy;
4556 	} else {
4557 		data->endselx = xx;
4558 		data->endsely = yy;
4559 	}
4560 }
4561 
4562 static void
window_copy_synchronize_cursor(struct window_mode_entry * wme,int no_reset)4563 window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset)
4564 {
4565 	struct window_copy_mode_data	*data = wme->data;
4566 
4567 	switch (data->cursordrag) {
4568 	case CURSORDRAG_ENDSEL:
4569 		window_copy_synchronize_cursor_end(wme, 0, no_reset);
4570 		break;
4571 	case CURSORDRAG_SEL:
4572 		window_copy_synchronize_cursor_end(wme, 1, no_reset);
4573 		break;
4574 	case CURSORDRAG_NONE:
4575 		break;
4576 	}
4577 }
4578 
4579 static void
window_copy_update_cursor(struct window_mode_entry * wme,u_int cx,u_int cy)4580 window_copy_update_cursor(struct window_mode_entry *wme, u_int cx, u_int cy)
4581 {
4582 	struct window_pane		*wp = wme->wp;
4583 	struct window_copy_mode_data	*data = wme->data;
4584 	struct screen			*s = &data->screen;
4585 	struct screen_write_ctx		 ctx;
4586 	u_int				 old_cx, old_cy;
4587 
4588 	old_cx = data->cx; old_cy = data->cy;
4589 	data->cx = cx; data->cy = cy;
4590 	if (old_cx == screen_size_x(s))
4591 		window_copy_redraw_lines(wme, old_cy, 1);
4592 	if (data->cx == screen_size_x(s))
4593 		window_copy_redraw_lines(wme, data->cy, 1);
4594 	else {
4595 		screen_write_start_pane(&ctx, wp, NULL);
4596 		screen_write_cursormove(&ctx, data->cx, data->cy, 0);
4597 		screen_write_stop(&ctx);
4598 	}
4599 }
4600 
4601 static void
window_copy_start_selection(struct window_mode_entry * wme)4602 window_copy_start_selection(struct window_mode_entry *wme)
4603 {
4604 	struct window_copy_mode_data	*data = wme->data;
4605 
4606 	data->selx = data->cx;
4607 	data->sely = screen_hsize(data->backing) + data->cy - data->oy;
4608 
4609 	data->endselx = data->selx;
4610 	data->endsely = data->sely;
4611 
4612 	data->cursordrag = CURSORDRAG_ENDSEL;
4613 
4614 	window_copy_set_selection(wme, 1, 0);
4615 }
4616 
4617 static int
window_copy_adjust_selection(struct window_mode_entry * wme,u_int * selx,u_int * sely)4618 window_copy_adjust_selection(struct window_mode_entry *wme, u_int *selx,
4619     u_int *sely)
4620 {
4621 	struct window_copy_mode_data	*data = wme->data;
4622 	struct screen			*s = &data->screen;
4623 	u_int 				 sx, sy, ty;
4624 	int				 relpos;
4625 
4626 	sx = *selx;
4627 	sy = *sely;
4628 
4629 	ty = screen_hsize(data->backing) - data->oy;
4630 	if (sy < ty) {
4631 		relpos = WINDOW_COPY_REL_POS_ABOVE;
4632 		if (!data->rectflag)
4633 			sx = 0;
4634 		sy = 0;
4635 	} else if (sy > ty + screen_size_y(s) - 1) {
4636 		relpos = WINDOW_COPY_REL_POS_BELOW;
4637 		if (!data->rectflag)
4638 			sx = screen_size_x(s) - 1;
4639 		sy = screen_size_y(s) - 1;
4640 	} else {
4641 		relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
4642 		sy -= ty;
4643 	}
4644 
4645 	*selx = sx;
4646 	*sely = sy;
4647 	return (relpos);
4648 }
4649 
4650 static int
window_copy_update_selection(struct window_mode_entry * wme,int may_redraw,int no_reset)4651 window_copy_update_selection(struct window_mode_entry *wme, int may_redraw,
4652     int no_reset)
4653 {
4654 	struct window_copy_mode_data	*data = wme->data;
4655 	struct screen			*s = &data->screen;
4656 
4657 	if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
4658 		return (0);
4659 	return (window_copy_set_selection(wme, may_redraw, no_reset));
4660 }
4661 
4662 static int
window_copy_set_selection(struct window_mode_entry * wme,int may_redraw,int no_reset)4663 window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
4664     int no_reset)
4665 {
4666 	struct window_pane		*wp = wme->wp;
4667 	struct window_copy_mode_data	*data = wme->data;
4668 	struct screen			*s = &data->screen;
4669 	struct options			*oo = wp->window->options;
4670 	struct grid_cell		 gc;
4671 	u_int				 sx, sy, cy, endsx, endsy;
4672 	int				 startrelpos, endrelpos;
4673 	struct format_tree		*ft;
4674 
4675 	window_copy_synchronize_cursor(wme, no_reset);
4676 
4677 	/* Adjust the selection. */
4678 	sx = data->selx;
4679 	sy = data->sely;
4680 	startrelpos = window_copy_adjust_selection(wme, &sx, &sy);
4681 
4682 	/* Adjust the end of selection. */
4683 	endsx = data->endselx;
4684 	endsy = data->endsely;
4685 	endrelpos = window_copy_adjust_selection(wme, &endsx, &endsy);
4686 
4687 	/* Selection is outside of the current screen */
4688 	if (startrelpos == endrelpos &&
4689 	    startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
4690 		screen_hide_selection(s);
4691 		return (0);
4692 	}
4693 
4694 	/* Set colours and selection. */
4695 	ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
4696 	style_apply(&gc, oo, "copy-mode-selection-style", ft);
4697 	gc.flags |= GRID_FLAG_NOPALETTE;
4698 	format_free(ft);
4699 	screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag,
4700 	    data->modekeys, &gc);
4701 
4702 	if (data->rectflag && may_redraw) {
4703 		/*
4704 		 * Can't rely on the caller to redraw the right lines for
4705 		 * rectangle selection - find the highest line and the number
4706 		 * of lines, and redraw just past that in both directions
4707 		 */
4708 		cy = data->cy;
4709 		if (data->cursordrag == CURSORDRAG_ENDSEL) {
4710 			if (sy < cy)
4711 				window_copy_redraw_lines(wme, sy, cy - sy + 1);
4712 			else
4713 				window_copy_redraw_lines(wme, cy, sy - cy + 1);
4714 		} else {
4715 			if (endsy < cy) {
4716 				window_copy_redraw_lines(wme, endsy,
4717 				    cy - endsy + 1);
4718 			} else {
4719 				window_copy_redraw_lines(wme, cy,
4720 				    endsy - cy + 1);
4721 			}
4722 		}
4723 	}
4724 
4725 	return (1);
4726 }
4727 
4728 static void *
window_copy_get_selection(struct window_mode_entry * wme,size_t * len)4729 window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
4730 {
4731 	struct window_pane		*wp = wme->wp;
4732 	struct window_copy_mode_data	*data = wme->data;
4733 	struct screen			*s = &data->screen;
4734 	char				*buf;
4735 	size_t				 off;
4736 	u_int				 i, xx, yy, sx, sy, ex, ey, ey_last;
4737 	u_int				 firstsx, lastex, restex, restsx, selx;
4738 	int				 keys;
4739 
4740 	if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) {
4741 		buf = window_copy_match_at_cursor(data);
4742 		if (buf != NULL)
4743 			*len = strlen(buf);
4744 		else
4745 			*len = 0;
4746 		return (buf);
4747 	}
4748 
4749 	buf = xmalloc(1);
4750 	off = 0;
4751 
4752 	*buf = '\0';
4753 
4754 	/*
4755 	 * The selection extends from selx,sely to (adjusted) cx,cy on
4756 	 * the base screen.
4757 	 */
4758 
4759 	/* Find start and end. */
4760 	xx = data->endselx;
4761 	yy = data->endsely;
4762 	if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
4763 		sx = xx; sy = yy;
4764 		ex = data->selx; ey = data->sely;
4765 	} else {
4766 		sx = data->selx; sy = data->sely;
4767 		ex = xx; ey = yy;
4768 	}
4769 
4770 	/* Trim ex to end of line. */
4771 	ey_last = window_copy_find_length(wme, ey);
4772 	if (ex > ey_last)
4773 		ex = ey_last;
4774 
4775 	/*
4776 	 * Deal with rectangle-copy if necessary; four situations: start of
4777 	 * first line (firstsx), end of last line (lastex), start (restsx) and
4778 	 * end (restex) of all other lines.
4779 	 */
4780 	xx = screen_size_x(s);
4781 
4782 	/*
4783 	 * Behave according to mode-keys. If it is emacs, copy like emacs,
4784 	 * keeping the top-left-most character, and dropping the
4785 	 * bottom-right-most, regardless of copy direction. If it is vi, also
4786 	 * keep bottom-right-most character.
4787 	 */
4788 	keys = options_get_number(wp->window->options, "mode-keys");
4789 	if (data->rectflag) {
4790 		/*
4791 		 * Need to ignore the column with the cursor in it, which for
4792 		 * rectangular copy means knowing which side the cursor is on.
4793 		 */
4794 		if (data->cursordrag == CURSORDRAG_ENDSEL)
4795 			selx = data->selx;
4796 		else
4797 			selx = data->endselx;
4798 		if (selx < data->cx) {
4799 			/* Selection start is on the left. */
4800 			if (keys == MODEKEY_EMACS) {
4801 				lastex = data->cx;
4802 				restex = data->cx;
4803 			}
4804 			else {
4805 				lastex = data->cx + 1;
4806 				restex = data->cx + 1;
4807 			}
4808 			firstsx = selx;
4809 			restsx = selx;
4810 		} else {
4811 			/* Cursor is on the left. */
4812 			lastex = selx + 1;
4813 			restex = selx + 1;
4814 			firstsx = data->cx;
4815 			restsx = data->cx;
4816 		}
4817 	} else {
4818 		if (keys == MODEKEY_EMACS)
4819 			lastex = ex;
4820 		else
4821 			lastex = ex + 1;
4822 		restex = xx;
4823 		firstsx = sx;
4824 		restsx = 0;
4825 	}
4826 
4827 	/* Copy the lines. */
4828 	for (i = sy; i <= ey; i++) {
4829 		window_copy_copy_line(wme, &buf, &off, i,
4830 		    (i == sy ? firstsx : restsx),
4831 		    (i == ey ? lastex : restex));
4832 	}
4833 
4834 	/* Don't bother if no data. */
4835 	if (off == 0) {
4836 		free(buf);
4837 		*len = 0;
4838 		return (NULL);
4839 	}
4840 	 /* Remove final \n (unless at end in vi mode). */
4841 	if (keys == MODEKEY_EMACS || lastex <= ey_last) {
4842 		if (~grid_get_line(data->backing->grid, ey)->flags &
4843 		    GRID_LINE_WRAPPED || lastex != ey_last)
4844 			off -= 1;
4845 	}
4846 	*len = off;
4847 	return (buf);
4848 }
4849 
4850 static void
window_copy_copy_buffer(struct window_mode_entry * wme,const char * prefix,void * buf,size_t len,int set_paste,int set_clip)4851 window_copy_copy_buffer(struct window_mode_entry *wme, const char *prefix,
4852     void *buf, size_t len, int set_paste, int set_clip)
4853 {
4854 	struct window_pane	*wp = wme->wp;
4855 	struct screen_write_ctx	 ctx;
4856 
4857 	if (set_clip &&
4858 	    options_get_number(global_options, "set-clipboard") != 0) {
4859 		screen_write_start_pane(&ctx, wp, NULL);
4860 		screen_write_setselection(&ctx, "", buf, len);
4861 		screen_write_stop(&ctx);
4862 		notify_pane("pane-set-clipboard", wp);
4863 	}
4864 
4865 	if (set_paste)
4866 		paste_add(prefix, buf, len);
4867 }
4868 
4869 static void *
window_copy_pipe_run(struct window_mode_entry * wme,struct session * s,const char * cmd,size_t * len)4870 window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
4871     const char *cmd, size_t *len)
4872 {
4873 	void		*buf;
4874 	struct job	*job;
4875 
4876 	buf = window_copy_get_selection(wme, len);
4877 	if (cmd == NULL || *cmd == '\0')
4878 		cmd = options_get_string(global_options, "copy-command");
4879 	if (cmd != NULL && *cmd != '\0') {
4880 		job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL,
4881 		    NULL, JOB_NOWAIT, -1, -1);
4882 		bufferevent_write(job_get_event(job), buf, *len);
4883 	}
4884 	return (buf);
4885 }
4886 
4887 static void
window_copy_pipe(struct window_mode_entry * wme,struct session * s,const char * cmd)4888 window_copy_pipe(struct window_mode_entry *wme, struct session *s,
4889     const char *cmd)
4890 {
4891 	size_t	len;
4892 
4893 	window_copy_pipe_run(wme, s, cmd, &len);
4894 }
4895 
4896 static void
window_copy_copy_pipe(struct window_mode_entry * wme,struct session * s,const char * prefix,const char * cmd,int set_paste,int set_clip)4897 window_copy_copy_pipe(struct window_mode_entry *wme, struct session *s,
4898     const char *prefix, const char *cmd, int set_paste, int set_clip)
4899 {
4900 	void	*buf;
4901 	size_t	 len;
4902 
4903 	buf = window_copy_pipe_run(wme, s, cmd, &len);
4904 	if (buf != NULL)
4905 		window_copy_copy_buffer(wme, prefix, buf, len, set_paste,
4906 		    set_clip);
4907 }
4908 
4909 static void
window_copy_copy_selection(struct window_mode_entry * wme,const char * prefix,int set_paste,int set_clip)4910 window_copy_copy_selection(struct window_mode_entry *wme, const char *prefix,
4911     int set_paste, int set_clip)
4912 {
4913 	char	*buf;
4914 	size_t	 len;
4915 
4916 	buf = window_copy_get_selection(wme, &len);
4917 	if (buf != NULL)
4918 		window_copy_copy_buffer(wme, prefix, buf, len, set_paste,
4919 		    set_clip);
4920 }
4921 
4922 static void
window_copy_append_selection(struct window_mode_entry * wme)4923 window_copy_append_selection(struct window_mode_entry *wme)
4924 {
4925 	struct window_pane		*wp = wme->wp;
4926 	char				*buf;
4927 	struct paste_buffer		*pb;
4928 	const char			*bufdata, *bufname = NULL;
4929 	size_t				 len, bufsize;
4930 	struct screen_write_ctx		 ctx;
4931 
4932 	buf = window_copy_get_selection(wme, &len);
4933 	if (buf == NULL)
4934 		return;
4935 
4936 	if (options_get_number(global_options, "set-clipboard") != 0) {
4937 		screen_write_start_pane(&ctx, wp, NULL);
4938 		screen_write_setselection(&ctx, "", buf, len);
4939 		screen_write_stop(&ctx);
4940 		notify_pane("pane-set-clipboard", wp);
4941 	}
4942 
4943 	pb = paste_get_top(&bufname);
4944 	if (pb != NULL) {
4945 		bufdata = paste_buffer_data(pb, &bufsize);
4946 		buf = xrealloc(buf, len + bufsize);
4947 		memmove(buf + bufsize, buf, len);
4948 		memcpy(buf, bufdata, bufsize);
4949 		len += bufsize;
4950 	}
4951 	if (paste_set(buf, len, bufname, NULL) != 0)
4952 		free(buf);
4953 }
4954 
4955 static void
window_copy_copy_line(struct window_mode_entry * wme,char ** buf,size_t * off,u_int sy,u_int sx,u_int ex)4956 window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
4957     u_int sy, u_int sx, u_int ex)
4958 {
4959 	struct window_copy_mode_data	*data = wme->data;
4960 	struct grid			*gd = data->backing->grid;
4961 	struct grid_cell		 gc;
4962 	struct grid_line		*gl;
4963 	struct utf8_data		 ud;
4964 	u_int				 i, xx, wrapped = 0;
4965 	const char			*s;
4966 
4967 	if (sx > ex)
4968 		return;
4969 
4970 	/*
4971 	 * Work out if the line was wrapped at the screen edge and all of it is
4972 	 * on screen.
4973 	 */
4974 	gl = grid_get_line(gd, sy);
4975 	if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
4976 		wrapped = 1;
4977 
4978 	/* If the line was wrapped, don't strip spaces (use the full length). */
4979 	if (wrapped)
4980 		xx = gl->cellsize;
4981 	else
4982 		xx = window_copy_find_length(wme, sy);
4983 	if (ex > xx)
4984 		ex = xx;
4985 	if (sx > xx)
4986 		sx = xx;
4987 
4988 	if (sx < ex) {
4989 		for (i = sx; i < ex; i++) {
4990 			grid_get_cell(gd, i, sy, &gc);
4991 			if (gc.flags & GRID_FLAG_PADDING)
4992 				continue;
4993 			if (gc.flags & GRID_FLAG_TAB)
4994 				utf8_set(&ud, '\t');
4995 			else
4996 				utf8_copy(&ud, &gc.data);
4997 			if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
4998 				s = tty_acs_get(NULL, ud.data[0]);
4999 				if (s != NULL && strlen(s) <= sizeof ud.data) {
5000 					ud.size = strlen(s);
5001 					memcpy(ud.data, s, ud.size);
5002 				}
5003 			}
5004 
5005 			*buf = xrealloc(*buf, (*off) + ud.size);
5006 			memcpy(*buf + *off, ud.data, ud.size);
5007 			*off += ud.size;
5008 		}
5009 	}
5010 
5011 	/* Only add a newline if the line wasn't wrapped. */
5012 	if (!wrapped || ex != xx) {
5013 		*buf = xrealloc(*buf, (*off) + 1);
5014 		(*buf)[(*off)++] = '\n';
5015 	}
5016 }
5017 
5018 static void
window_copy_clear_selection(struct window_mode_entry * wme)5019 window_copy_clear_selection(struct window_mode_entry *wme)
5020 {
5021 	struct window_copy_mode_data   *data = wme->data;
5022 	u_int				px, py;
5023 
5024 	screen_clear_selection(&data->screen);
5025 
5026 	data->cursordrag = CURSORDRAG_NONE;
5027 	data->lineflag = LINE_SEL_NONE;
5028 	data->selflag = SEL_CHAR;
5029 
5030 	py = screen_hsize(data->backing) + data->cy - data->oy;
5031 	px = window_copy_find_length(wme, py);
5032 	if (data->cx > px)
5033 		window_copy_update_cursor(wme, px, data->cy);
5034 }
5035 
5036 static int
window_copy_in_set(struct window_mode_entry * wme,u_int px,u_int py,const char * set)5037 window_copy_in_set(struct window_mode_entry *wme, u_int px, u_int py,
5038     const char *set)
5039 {
5040 	struct window_copy_mode_data	*data = wme->data;
5041 
5042 	return (grid_in_set(data->backing->grid, px, py, set));
5043 }
5044 
5045 static u_int
window_copy_find_length(struct window_mode_entry * wme,u_int py)5046 window_copy_find_length(struct window_mode_entry *wme, u_int py)
5047 {
5048 	struct window_copy_mode_data	*data = wme->data;
5049 
5050 	return (grid_line_length(data->backing->grid, py));
5051 }
5052 
5053 static void
window_copy_cursor_start_of_line(struct window_mode_entry * wme)5054 window_copy_cursor_start_of_line(struct window_mode_entry *wme)
5055 {
5056 	struct window_copy_mode_data	*data = wme->data;
5057 	struct screen			*back_s = data->backing;
5058 	struct grid_reader		 gr;
5059 	u_int				 px, py, oldy, hsize;
5060 
5061 	px = data->cx;
5062 	hsize = screen_hsize(back_s);
5063 	py = hsize + data->cy - data->oy;
5064 	oldy = data->cy;
5065 
5066 	grid_reader_start(&gr, back_s->grid, px, py);
5067 	grid_reader_cursor_start_of_line(&gr, 1);
5068 	grid_reader_get_cursor(&gr, &px, &py);
5069 	window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5070 }
5071 
5072 static void
window_copy_cursor_back_to_indentation(struct window_mode_entry * wme)5073 window_copy_cursor_back_to_indentation(struct window_mode_entry *wme)
5074 {
5075 	struct window_copy_mode_data	*data = wme->data;
5076 	struct screen			*back_s = data->backing;
5077 	struct grid_reader		 gr;
5078 	u_int				 px, py, oldy, hsize;
5079 
5080 	px = data->cx;
5081 	hsize = screen_hsize(back_s);
5082 	py = hsize + data->cy - data->oy;
5083 	oldy = data->cy;
5084 
5085 	grid_reader_start(&gr, back_s->grid, px, py);
5086 	grid_reader_cursor_back_to_indentation(&gr);
5087 	grid_reader_get_cursor(&gr, &px, &py);
5088 	window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5089 }
5090 
5091 static void
window_copy_cursor_end_of_line(struct window_mode_entry * wme)5092 window_copy_cursor_end_of_line(struct window_mode_entry *wme)
5093 {
5094 	struct window_copy_mode_data	*data = wme->data;
5095 	struct screen			*back_s = data->backing;
5096 	struct grid_reader		 gr;
5097 	u_int				 px, py, oldy, hsize;
5098 
5099 	px = data->cx;
5100 	hsize = screen_hsize(back_s);
5101 	py =  hsize + data->cy - data->oy;
5102 	oldy = data->cy;
5103 
5104 	grid_reader_start(&gr, back_s->grid, px, py);
5105 	if (data->screen.sel != NULL && data->rectflag)
5106 		grid_reader_cursor_end_of_line(&gr, 1, 1);
5107 	else
5108 		grid_reader_cursor_end_of_line(&gr, 1, 0);
5109 	grid_reader_get_cursor(&gr, &px, &py);
5110 	window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5111 	    data->oy, oldy, px, py, 0);
5112 }
5113 
5114 static void
window_copy_other_end(struct window_mode_entry * wme)5115 window_copy_other_end(struct window_mode_entry *wme)
5116 {
5117 	struct window_copy_mode_data	*data = wme->data;
5118 	struct screen			*s = &data->screen;
5119 	u_int				 selx, sely, cy, yy, hsize;
5120 
5121 	if (s->sel == NULL && data->lineflag == LINE_SEL_NONE)
5122 		return;
5123 
5124 	if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5125 		data->lineflag = LINE_SEL_RIGHT_LEFT;
5126 	else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5127 		data->lineflag = LINE_SEL_LEFT_RIGHT;
5128 
5129 	switch (data->cursordrag) {
5130 		case CURSORDRAG_NONE:
5131 		case CURSORDRAG_SEL:
5132 			data->cursordrag = CURSORDRAG_ENDSEL;
5133 			break;
5134 		case CURSORDRAG_ENDSEL:
5135 			data->cursordrag = CURSORDRAG_SEL;
5136 			break;
5137 	}
5138 
5139 	selx = data->endselx;
5140 	sely = data->endsely;
5141 	if (data->cursordrag == CURSORDRAG_SEL) {
5142 		selx = data->selx;
5143 		sely = data->sely;
5144 	}
5145 
5146 	cy = data->cy;
5147 	yy = screen_hsize(data->backing) + data->cy - data->oy;
5148 
5149 	data->cx = selx;
5150 
5151 	hsize = screen_hsize(data->backing);
5152 	if (sely < hsize - data->oy) { /* above */
5153 		data->oy = hsize - sely;
5154 		data->cy = 0;
5155 	} else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
5156 		data->oy = hsize - sely + screen_size_y(s) - 1;
5157 		data->cy = screen_size_y(s) - 1;
5158 	} else
5159 		data->cy = cy + sely - yy;
5160 
5161 	window_copy_update_selection(wme, 1, 1);
5162 	window_copy_redraw_screen(wme);
5163 }
5164 
5165 static void
window_copy_cursor_left(struct window_mode_entry * wme)5166 window_copy_cursor_left(struct window_mode_entry *wme)
5167 {
5168 	struct window_copy_mode_data	*data = wme->data;
5169 	struct screen			*back_s = data->backing;
5170 	struct grid_reader		 gr;
5171 	u_int				 px, py, oldy, hsize;
5172 
5173 	px = data->cx;
5174 	hsize = screen_hsize(back_s);
5175 	py = hsize + data->cy - data->oy;
5176 	oldy = data->cy;
5177 
5178 	grid_reader_start(&gr, back_s->grid, px, py);
5179 	grid_reader_cursor_left(&gr, 1);
5180 	grid_reader_get_cursor(&gr, &px, &py);
5181 	window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5182 }
5183 
5184 static void
window_copy_cursor_right(struct window_mode_entry * wme,int all)5185 window_copy_cursor_right(struct window_mode_entry *wme, int all)
5186 {
5187 	struct window_copy_mode_data	*data = wme->data;
5188 	struct screen			*back_s = data->backing;
5189 	struct grid_reader		 gr;
5190 	u_int				 px, py, oldy, hsize;
5191 
5192 	px = data->cx;
5193 	hsize = screen_hsize(back_s);
5194 	py = hsize + data->cy - data->oy;
5195 	oldy = data->cy;
5196 
5197 	grid_reader_start(&gr, back_s->grid, px, py);
5198 	grid_reader_cursor_right(&gr, 1, all);
5199 	grid_reader_get_cursor(&gr, &px, &py);
5200 	window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5201 	    data->oy, oldy, px, py, 0);
5202 }
5203 
5204 static void
window_copy_cursor_up(struct window_mode_entry * wme,int scroll_only)5205 window_copy_cursor_up(struct window_mode_entry *wme, int scroll_only)
5206 {
5207 	struct window_copy_mode_data	*data = wme->data;
5208 	struct screen			*s = &data->screen;
5209 	u_int				 ox, oy, px, py;
5210 	int				 norectsel;
5211 
5212 	norectsel = data->screen.sel == NULL || !data->rectflag;
5213 	oy = screen_hsize(data->backing) + data->cy - data->oy;
5214 	ox = window_copy_find_length(wme, oy);
5215 	if (norectsel && data->cx != ox) {
5216 		data->lastcx = data->cx;
5217 		data->lastsx = ox;
5218 	}
5219 
5220 	if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
5221 		window_copy_other_end(wme);
5222 
5223 	if (scroll_only || data->cy == 0) {
5224 		if (norectsel)
5225 			data->cx = data->lastcx;
5226 		window_copy_scroll_down(wme, 1);
5227 		if (scroll_only) {
5228 			if (data->cy == screen_size_y(s) - 1)
5229 				window_copy_redraw_lines(wme, data->cy, 1);
5230 			else
5231 				window_copy_redraw_lines(wme, data->cy, 2);
5232 		}
5233 	} else {
5234 		if (norectsel) {
5235 			window_copy_update_cursor(wme, data->lastcx,
5236 			    data->cy - 1);
5237 		} else
5238 			window_copy_update_cursor(wme, data->cx, data->cy - 1);
5239 		if (window_copy_update_selection(wme, 1, 0)) {
5240 			if (data->cy == screen_size_y(s) - 1)
5241 				window_copy_redraw_lines(wme, data->cy, 1);
5242 			else
5243 				window_copy_redraw_lines(wme, data->cy, 2);
5244 		}
5245 	}
5246 
5247 	if (norectsel) {
5248 		py = screen_hsize(data->backing) + data->cy - data->oy;
5249 		px = window_copy_find_length(wme, py);
5250 		if ((data->cx >= data->lastsx && data->cx != px) ||
5251 		    data->cx > px)
5252 		{
5253 			window_copy_update_cursor(wme, px, data->cy);
5254 			if (window_copy_update_selection(wme, 1, 0))
5255 				window_copy_redraw_lines(wme, data->cy, 1);
5256 		}
5257 	}
5258 
5259 	if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5260 	{
5261 		py = screen_hsize(data->backing) + data->cy - data->oy;
5262 		if (data->rectflag)
5263 			px = screen_size_x(data->backing);
5264 		else
5265 			px = window_copy_find_length(wme, py);
5266 		window_copy_update_cursor(wme, px, data->cy);
5267 		if (window_copy_update_selection(wme, 1, 0))
5268 			window_copy_redraw_lines(wme, data->cy, 1);
5269 	}
5270 	else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5271 	{
5272 		window_copy_update_cursor(wme, 0, data->cy);
5273 		if (window_copy_update_selection(wme, 1, 0))
5274 			window_copy_redraw_lines(wme, data->cy, 1);
5275 	}
5276 }
5277 
5278 static void
window_copy_cursor_down(struct window_mode_entry * wme,int scroll_only)5279 window_copy_cursor_down(struct window_mode_entry *wme, int scroll_only)
5280 {
5281 	struct window_copy_mode_data	*data = wme->data;
5282 	struct screen			*s = &data->screen;
5283 	u_int				 ox, oy, px, py;
5284 	int				 norectsel;
5285 
5286 	norectsel = data->screen.sel == NULL || !data->rectflag;
5287 	oy = screen_hsize(data->backing) + data->cy - data->oy;
5288 	ox = window_copy_find_length(wme, oy);
5289 	if (norectsel && data->cx != ox) {
5290 		data->lastcx = data->cx;
5291 		data->lastsx = ox;
5292 	}
5293 
5294 	if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
5295 		window_copy_other_end(wme);
5296 
5297 	if (scroll_only || data->cy == screen_size_y(s) - 1) {
5298 		if (norectsel)
5299 			data->cx = data->lastcx;
5300 		window_copy_scroll_up(wme, 1);
5301 		if (scroll_only && data->cy > 0)
5302 			window_copy_redraw_lines(wme, data->cy - 1, 2);
5303 	} else {
5304 		if (norectsel) {
5305 			window_copy_update_cursor(wme, data->lastcx,
5306 			    data->cy + 1);
5307 		} else
5308 			window_copy_update_cursor(wme, data->cx, data->cy + 1);
5309 		if (window_copy_update_selection(wme, 1, 0))
5310 			window_copy_redraw_lines(wme, data->cy - 1, 2);
5311 	}
5312 
5313 	if (norectsel) {
5314 		py = screen_hsize(data->backing) + data->cy - data->oy;
5315 		px = window_copy_find_length(wme, py);
5316 		if ((data->cx >= data->lastsx && data->cx != px) ||
5317 		    data->cx > px)
5318 		{
5319 			window_copy_update_cursor(wme, px, data->cy);
5320 			if (window_copy_update_selection(wme, 1, 0))
5321 				window_copy_redraw_lines(wme, data->cy, 1);
5322 		}
5323 	}
5324 
5325 	if (data->lineflag == LINE_SEL_LEFT_RIGHT)
5326 	{
5327 		py = screen_hsize(data->backing) + data->cy - data->oy;
5328 		if (data->rectflag)
5329 			px = screen_size_x(data->backing);
5330 		else
5331 			px = window_copy_find_length(wme, py);
5332 		window_copy_update_cursor(wme, px, data->cy);
5333 		if (window_copy_update_selection(wme, 1, 0))
5334 			window_copy_redraw_lines(wme, data->cy, 1);
5335 	}
5336 	else if (data->lineflag == LINE_SEL_RIGHT_LEFT)
5337 	{
5338 		window_copy_update_cursor(wme, 0, data->cy);
5339 		if (window_copy_update_selection(wme, 1, 0))
5340 			window_copy_redraw_lines(wme, data->cy, 1);
5341 	}
5342 }
5343 
5344 static void
window_copy_cursor_jump(struct window_mode_entry * wme)5345 window_copy_cursor_jump(struct window_mode_entry *wme)
5346 {
5347 	struct window_copy_mode_data	*data = wme->data;
5348 	struct screen			*back_s = data->backing;
5349 	struct grid_reader		 gr;
5350 	u_int				 px, py, oldy, hsize;
5351 
5352 	px = data->cx + 1;
5353 	hsize = screen_hsize(back_s);
5354 	py = hsize + data->cy - data->oy;
5355 	oldy = data->cy;
5356 
5357 	grid_reader_start(&gr, back_s->grid, px, py);
5358 	if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5359 		grid_reader_get_cursor(&gr, &px, &py);
5360 		window_copy_acquire_cursor_down(wme, hsize,
5361 		    screen_size_y(back_s), data->oy, oldy, px, py, 0);
5362 	}
5363 }
5364 
5365 static void
window_copy_cursor_jump_back(struct window_mode_entry * wme)5366 window_copy_cursor_jump_back(struct window_mode_entry *wme)
5367 {
5368 	struct window_copy_mode_data	*data = wme->data;
5369 	struct screen			*back_s = data->backing;
5370 	struct grid_reader		 gr;
5371 	u_int				 px, py, oldy, hsize;
5372 
5373 	px = data->cx;
5374 	hsize = screen_hsize(back_s);
5375 	py = hsize + data->cy - data->oy;
5376 	oldy = data->cy;
5377 
5378 	grid_reader_start(&gr, back_s->grid, px, py);
5379 	grid_reader_cursor_left(&gr, 0);
5380 	if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5381 		grid_reader_get_cursor(&gr, &px, &py);
5382 		window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5383 		    py);
5384 	}
5385 }
5386 
5387 static void
window_copy_cursor_jump_to(struct window_mode_entry * wme)5388 window_copy_cursor_jump_to(struct window_mode_entry *wme)
5389 {
5390 	struct window_copy_mode_data	*data = wme->data;
5391 	struct screen			*back_s = data->backing;
5392 	struct grid_reader		 gr;
5393 	u_int				 px, py, oldy, hsize;
5394 
5395 	px = data->cx + 2;
5396 	hsize = screen_hsize(back_s);
5397 	py = hsize + data->cy - data->oy;
5398 	oldy = data->cy;
5399 
5400 	grid_reader_start(&gr, back_s->grid, px, py);
5401 	if (grid_reader_cursor_jump(&gr, data->jumpchar)) {
5402 		grid_reader_cursor_left(&gr, 1);
5403 		grid_reader_get_cursor(&gr, &px, &py);
5404 		window_copy_acquire_cursor_down(wme, hsize,
5405 		    screen_size_y(back_s), data->oy, oldy, px, py, 0);
5406 	}
5407 }
5408 
5409 static void
window_copy_cursor_jump_to_back(struct window_mode_entry * wme)5410 window_copy_cursor_jump_to_back(struct window_mode_entry *wme)
5411 {
5412 	struct window_copy_mode_data	*data = wme->data;
5413 	struct screen			*back_s = data->backing;
5414 	struct grid_reader		 gr;
5415 	u_int				 px, py, oldy, hsize;
5416 
5417 	px = data->cx;
5418 	hsize = screen_hsize(back_s);
5419 	py = hsize + data->cy - data->oy;
5420 	oldy = data->cy;
5421 
5422 	grid_reader_start(&gr, back_s->grid, px, py);
5423 	grid_reader_cursor_left(&gr, 0);
5424 	grid_reader_cursor_left(&gr, 0);
5425 	if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) {
5426 		grid_reader_cursor_right(&gr, 1, 0);
5427 		grid_reader_get_cursor(&gr, &px, &py);
5428 		window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px,
5429 		    py);
5430 	}
5431 }
5432 
5433 static void
window_copy_cursor_next_word(struct window_mode_entry * wme,const char * separators)5434 window_copy_cursor_next_word(struct window_mode_entry *wme,
5435     const char *separators)
5436 {
5437 	struct window_copy_mode_data	*data = wme->data;
5438 	struct screen			*back_s = data->backing;
5439 	struct grid_reader		 gr;
5440 	u_int				 px, py, oldy, hsize;
5441 
5442 	px = data->cx;
5443 	hsize = screen_hsize(back_s);
5444 	py =  hsize + data->cy - data->oy;
5445 	oldy = data->cy;
5446 
5447 	grid_reader_start(&gr, back_s->grid, px, py);
5448 	grid_reader_cursor_next_word(&gr, separators);
5449 	grid_reader_get_cursor(&gr, &px, &py);
5450 	window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5451 	    data->oy, oldy, px, py, 0);
5452 }
5453 
5454 /* Compute the next place where a word ends. */
5455 static void
window_copy_cursor_next_word_end_pos(struct window_mode_entry * wme,const char * separators,u_int * ppx,u_int * ppy)5456 window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme,
5457     const char *separators, u_int *ppx, u_int *ppy)
5458 {
5459 	struct window_pane		*wp = wme->wp;
5460 	struct window_copy_mode_data	*data = wme->data;
5461 	struct options			*oo = wp->window->options;
5462 	struct screen			*back_s = data->backing;
5463 	struct grid_reader		 gr;
5464 	u_int				 px, py, hsize;
5465 
5466 	px = data->cx;
5467 	hsize = screen_hsize(back_s);
5468 	py =  hsize + data->cy - data->oy;
5469 
5470 	grid_reader_start(&gr, back_s->grid, px, py);
5471 	if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5472 		if (!grid_reader_in_set(&gr, WHITESPACE))
5473 			grid_reader_cursor_right(&gr, 0, 0);
5474 		grid_reader_cursor_next_word_end(&gr, separators);
5475 		grid_reader_cursor_left(&gr, 1);
5476 	} else
5477 		grid_reader_cursor_next_word_end(&gr, separators);
5478 	grid_reader_get_cursor(&gr, &px, &py);
5479 	*ppx = px;
5480 	*ppy = py;
5481 }
5482 
5483 /* Move to the next place where a word ends. */
5484 static void
window_copy_cursor_next_word_end(struct window_mode_entry * wme,const char * separators,int no_reset)5485 window_copy_cursor_next_word_end(struct window_mode_entry *wme,
5486     const char *separators, int no_reset)
5487 {
5488 	struct window_pane		*wp = wme->wp;
5489 	struct window_copy_mode_data	*data = wme->data;
5490 	struct options			*oo = wp->window->options;
5491 	struct screen			*back_s = data->backing;
5492 	struct grid_reader		 gr;
5493 	u_int				 px, py, oldy, hsize;
5494 
5495 	px = data->cx;
5496 	hsize = screen_hsize(back_s);
5497 	py =  hsize + data->cy - data->oy;
5498 	oldy = data->cy;
5499 
5500 	grid_reader_start(&gr, back_s->grid, px, py);
5501 	if (options_get_number(oo, "mode-keys") == MODEKEY_VI) {
5502 		if (!grid_reader_in_set(&gr, WHITESPACE))
5503 			grid_reader_cursor_right(&gr, 0, 0);
5504 		grid_reader_cursor_next_word_end(&gr, separators);
5505 		grid_reader_cursor_left(&gr, 1);
5506 	} else
5507 		grid_reader_cursor_next_word_end(&gr, separators);
5508 	grid_reader_get_cursor(&gr, &px, &py);
5509 	window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s),
5510 	    data->oy, oldy, px, py, no_reset);
5511 }
5512 
5513 /* Compute the previous place where a word begins. */
5514 static void
window_copy_cursor_previous_word_pos(struct window_mode_entry * wme,const char * separators,u_int * ppx,u_int * ppy)5515 window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
5516     const char *separators, u_int *ppx, u_int *ppy)
5517 {
5518 	struct window_copy_mode_data	*data = wme->data;
5519 	struct screen			*back_s = data->backing;
5520 	struct grid_reader		 gr;
5521 	u_int				 px, py, hsize;
5522 
5523 	px = data->cx;
5524 	hsize = screen_hsize(back_s);
5525 	py = hsize + data->cy - data->oy;
5526 
5527 	grid_reader_start(&gr, back_s->grid, px, py);
5528 	grid_reader_cursor_previous_word(&gr, separators, 0, 1);
5529 	grid_reader_get_cursor(&gr, &px, &py);
5530 	*ppx = px;
5531 	*ppy = py;
5532 }
5533 
5534 /* Move to the previous place where a word begins. */
5535 static void
window_copy_cursor_previous_word(struct window_mode_entry * wme,const char * separators,int already)5536 window_copy_cursor_previous_word(struct window_mode_entry *wme,
5537     const char *separators, int already)
5538 {
5539 	struct window_copy_mode_data	*data = wme->data;
5540 	struct window			*w = wme->wp->window;
5541 	struct screen			*back_s = data->backing;
5542 	struct grid_reader		 gr;
5543 	u_int				 px, py, oldy, hsize;
5544 	int				 stop_at_eol;
5545 
5546 	if (options_get_number(w->options, "mode-keys") == MODEKEY_EMACS)
5547 		stop_at_eol = 1;
5548 	else
5549 		stop_at_eol = 0;
5550 
5551 	px = data->cx;
5552 	hsize = screen_hsize(back_s);
5553 	py = hsize + data->cy - data->oy;
5554 	oldy = data->cy;
5555 
5556 	grid_reader_start(&gr, back_s->grid, px, py);
5557 	grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol);
5558 	grid_reader_get_cursor(&gr, &px, &py);
5559 	window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py);
5560 }
5561 
5562 static void
window_copy_cursor_prompt(struct window_mode_entry * wme,int direction,int start_output)5563 window_copy_cursor_prompt(struct window_mode_entry *wme, int direction,
5564     int start_output)
5565 {
5566 	struct window_copy_mode_data	*data = wme->data;
5567 	struct screen			*s = data->backing;
5568 	struct grid			*gd = s->grid;
5569 	u_int				 end_line;
5570 	u_int				 line = gd->hsize - data->oy + data->cy;
5571 	int				 add, line_flag;
5572 
5573 	if (start_output)
5574 		line_flag = GRID_LINE_START_OUTPUT;
5575 	else
5576 		line_flag = GRID_LINE_START_PROMPT;
5577 
5578 	if (direction == 0) { /* up */
5579 		add = -1;
5580 		end_line = 0;
5581 	} else { /* down */
5582 		add = 1;
5583 		end_line = gd->hsize + gd->sy - 1;
5584 	}
5585 
5586 	if (line == end_line)
5587 		return;
5588 	for (;;) {
5589 		if (line == end_line)
5590 			return;
5591 		line += add;
5592 
5593 		if (grid_get_line(gd, line)->flags & line_flag)
5594 			break;
5595 	}
5596 
5597 	data->cx = 0;
5598 	if (line > gd->hsize) {
5599 		data->cy = line - gd->hsize;
5600 		data->oy = 0;
5601 	} else {
5602 		data->cy = 0;
5603 		data->oy = gd->hsize - line;
5604 	}
5605 
5606 	window_copy_update_selection(wme, 1, 0);
5607 	window_copy_redraw_screen(wme);
5608 }
5609 
5610 static void
window_copy_scroll_up(struct window_mode_entry * wme,u_int ny)5611 window_copy_scroll_up(struct window_mode_entry *wme, u_int ny)
5612 {
5613 	struct window_pane		*wp = wme->wp;
5614 	struct window_copy_mode_data	*data = wme->data;
5615 	struct screen			*s = &data->screen;
5616 	struct screen_write_ctx		 ctx;
5617 
5618 	if (data->oy < ny)
5619 		ny = data->oy;
5620 	if (ny == 0)
5621 		return;
5622 	data->oy -= ny;
5623 
5624 	if (data->searchmark != NULL && !data->timeout)
5625 		window_copy_search_marks(wme, NULL, data->searchregex, 1);
5626 	window_copy_update_selection(wme, 0, 0);
5627 
5628 	screen_write_start_pane(&ctx, wp, NULL);
5629 	screen_write_cursormove(&ctx, 0, 0, 0);
5630 	screen_write_deleteline(&ctx, ny, 8);
5631 	window_copy_write_lines(wme, &ctx, screen_size_y(s) - ny, ny);
5632 	window_copy_write_line(wme, &ctx, 0);
5633 	if (screen_size_y(s) > 1)
5634 		window_copy_write_line(wme, &ctx, 1);
5635 	if (screen_size_y(s) > 3)
5636 		window_copy_write_line(wme, &ctx, screen_size_y(s) - 2);
5637 	if (s->sel != NULL && screen_size_y(s) > ny)
5638 		window_copy_write_line(wme, &ctx, screen_size_y(s) - ny - 1);
5639 	screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5640 	screen_write_stop(&ctx);
5641 	wp->flags |= PANE_REDRAWSCROLLBAR;
5642 }
5643 
5644 static void
window_copy_scroll_down(struct window_mode_entry * wme,u_int ny)5645 window_copy_scroll_down(struct window_mode_entry *wme, u_int ny)
5646 {
5647 	struct window_pane		*wp = wme->wp;
5648 	struct window_copy_mode_data	*data = wme->data;
5649 	struct screen			*s = &data->screen;
5650 	struct screen_write_ctx		 ctx;
5651 
5652 	if (ny > screen_hsize(data->backing))
5653 		return;
5654 
5655 	if (data->oy > screen_hsize(data->backing) - ny)
5656 		ny = screen_hsize(data->backing) - data->oy;
5657 	if (ny == 0)
5658 		return;
5659 	data->oy += ny;
5660 
5661 	if (data->searchmark != NULL && !data->timeout)
5662 		window_copy_search_marks(wme, NULL, data->searchregex, 1);
5663 	window_copy_update_selection(wme, 0, 0);
5664 
5665 	screen_write_start_pane(&ctx, wp, NULL);
5666 	screen_write_cursormove(&ctx, 0, 0, 0);
5667 	screen_write_insertline(&ctx, ny, 8);
5668 	window_copy_write_lines(wme, &ctx, 0, ny);
5669 	if (s->sel != NULL && screen_size_y(s) > ny)
5670 		window_copy_write_line(wme, &ctx, ny);
5671 	else if (ny == 1) /* nuke position */
5672 		window_copy_write_line(wme, &ctx, 1);
5673 	screen_write_cursormove(&ctx, data->cx, data->cy, 0);
5674 	screen_write_stop(&ctx);
5675 	wp->flags |= PANE_REDRAWSCROLLBAR;
5676 }
5677 
5678 static void
window_copy_rectangle_set(struct window_mode_entry * wme,int rectflag)5679 window_copy_rectangle_set(struct window_mode_entry *wme, int rectflag)
5680 {
5681 	struct window_copy_mode_data	*data = wme->data;
5682 	u_int				 px, py;
5683 
5684 	data->rectflag = rectflag;
5685 
5686 	py = screen_hsize(data->backing) + data->cy - data->oy;
5687 	px = window_copy_find_length(wme, py);
5688 	if (data->cx > px)
5689 		window_copy_update_cursor(wme, px, data->cy);
5690 
5691 	window_copy_update_selection(wme, 1, 0);
5692 	window_copy_redraw_screen(wme);
5693 }
5694 
5695 static void
window_copy_move_mouse(struct mouse_event * m)5696 window_copy_move_mouse(struct mouse_event *m)
5697 {
5698 	struct window_pane		*wp;
5699 	struct window_mode_entry	*wme;
5700 	u_int				 x, y;
5701 
5702 	wp = cmd_mouse_pane(m, NULL, NULL);
5703 	if (wp == NULL)
5704 		return;
5705 	wme = TAILQ_FIRST(&wp->modes);
5706 	if (wme == NULL)
5707 		return;
5708 	if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5709 		return;
5710 
5711 	if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5712 		return;
5713 
5714 	window_copy_update_cursor(wme, x, y);
5715 }
5716 
5717 void
window_copy_start_drag(struct client * c,struct mouse_event * m)5718 window_copy_start_drag(struct client *c, struct mouse_event *m)
5719 {
5720 	struct window_pane		*wp;
5721 	struct window_mode_entry	*wme;
5722 	struct window_copy_mode_data	*data;
5723 	u_int				 x, y, yg;
5724 
5725 	if (c == NULL)
5726 		return;
5727 
5728 	wp = cmd_mouse_pane(m, NULL, NULL);
5729 	if (wp == NULL)
5730 		return;
5731 	wme = TAILQ_FIRST(&wp->modes);
5732 	if (wme == NULL)
5733 		return;
5734 	if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5735 		return;
5736 
5737 	if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
5738 		return;
5739 
5740 	c->tty.mouse_drag_update = window_copy_drag_update;
5741 	c->tty.mouse_drag_release = window_copy_drag_release;
5742 
5743 	data = wme->data;
5744 	yg = screen_hsize(data->backing) + y - data->oy;
5745 	if (x < data->selrx || x > data->endselrx || yg != data->selry)
5746 		data->selflag = SEL_CHAR;
5747 	switch (data->selflag) {
5748 	case SEL_WORD:
5749 		if (data->separators != NULL) {
5750 			window_copy_update_cursor(wme, x, y);
5751 			window_copy_cursor_previous_word_pos(wme,
5752 			    data->separators, &x, &y);
5753 			y -= screen_hsize(data->backing) - data->oy;
5754 		}
5755 		window_copy_update_cursor(wme, x, y);
5756 		break;
5757 	case SEL_LINE:
5758 		window_copy_update_cursor(wme, 0, y);
5759 		break;
5760 	case SEL_CHAR:
5761 		window_copy_update_cursor(wme, x, y);
5762 		window_copy_start_selection(wme);
5763 		break;
5764 	}
5765 
5766 	window_copy_redraw_screen(wme);
5767 	window_copy_drag_update(c, m);
5768 }
5769 
5770 static void
window_copy_drag_update(struct client * c,struct mouse_event * m)5771 window_copy_drag_update(struct client *c, struct mouse_event *m)
5772 {
5773 	struct window_pane		*wp;
5774 	struct window_mode_entry	*wme;
5775 	struct window_copy_mode_data	*data;
5776 	u_int				 x, y, old_cx, old_cy;
5777 	struct timeval			 tv = {
5778 		.tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
5779 	};
5780 
5781 	if (c == NULL)
5782 		return;
5783 
5784 	wp = cmd_mouse_pane(m, NULL, NULL);
5785 	if (wp == NULL)
5786 		return;
5787 	wme = TAILQ_FIRST(&wp->modes);
5788 	if (wme == NULL)
5789 		return;
5790 	if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5791 		return;
5792 
5793 	data = wme->data;
5794 	evtimer_del(&data->dragtimer);
5795 
5796 	if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
5797 		return;
5798 	old_cx = data->cx;
5799 	old_cy = data->cy;
5800 
5801 	window_copy_update_cursor(wme, x, y);
5802 	if (window_copy_update_selection(wme, 1, 0))
5803 		window_copy_redraw_selection(wme, old_cy);
5804 	if (old_cy != data->cy || old_cx == data->cx) {
5805 		if (y == 0) {
5806 			evtimer_add(&data->dragtimer, &tv);
5807 			window_copy_cursor_up(wme, 1);
5808 		} else if (y == screen_size_y(&data->screen) - 1) {
5809 			evtimer_add(&data->dragtimer, &tv);
5810 			window_copy_cursor_down(wme, 1);
5811 		}
5812 	}
5813 }
5814 
5815 static void
window_copy_drag_release(struct client * c,struct mouse_event * m)5816 window_copy_drag_release(struct client *c, struct mouse_event *m)
5817 {
5818 	struct window_pane		*wp;
5819 	struct window_mode_entry	*wme;
5820 	struct window_copy_mode_data	*data;
5821 
5822 	if (c == NULL)
5823 		return;
5824 
5825 	wp = cmd_mouse_pane(m, NULL, NULL);
5826 	if (wp == NULL)
5827 		return;
5828 	wme = TAILQ_FIRST(&wp->modes);
5829 	if (wme == NULL)
5830 		return;
5831 	if (wme->mode != &window_copy_mode && wme->mode != &window_view_mode)
5832 		return;
5833 
5834 	data = wme->data;
5835 	evtimer_del(&data->dragtimer);
5836 }
5837 
5838 static void
window_copy_jump_to_mark(struct window_mode_entry * wme)5839 window_copy_jump_to_mark(struct window_mode_entry *wme)
5840 {
5841 	struct window_copy_mode_data	*data = wme->data;
5842 	u_int				 tmx, tmy;
5843 
5844 	tmx = data->cx;
5845 	tmy = screen_hsize(data->backing) + data->cy - data->oy;
5846 	data->cx = data->mx;
5847 	if (data->my < screen_hsize(data->backing)) {
5848 		data->cy = 0;
5849 		data->oy = screen_hsize(data->backing) - data->my;
5850 	} else {
5851 		data->cy = data->my - screen_hsize(data->backing);
5852 		data->oy = 0;
5853 	}
5854 	data->mx = tmx;
5855 	data->my = tmy;
5856 	data->showmark = 1;
5857 	window_copy_update_selection(wme, 0, 0);
5858 	window_copy_redraw_screen(wme);
5859 }
5860 
5861 /* Scroll up if the cursor went off the visible screen. */
5862 static void
window_copy_acquire_cursor_up(struct window_mode_entry * wme,u_int hsize,u_int oy,u_int oldy,u_int px,u_int py)5863 window_copy_acquire_cursor_up(struct window_mode_entry *wme, u_int hsize,
5864     u_int oy, u_int oldy, u_int px, u_int py)
5865 {
5866 	u_int	cy, yy, ny, nd;
5867 
5868 	yy = hsize - oy;
5869 	if (py < yy) {
5870 		ny = yy - py;
5871 		cy = 0;
5872 		nd = 1;
5873 	} else {
5874 		ny = 0;
5875 		cy = py - yy;
5876 		nd = oldy - cy + 1;
5877 	}
5878 	while (ny > 0) {
5879 		window_copy_cursor_up(wme, 1);
5880 		ny--;
5881 	}
5882 	window_copy_update_cursor(wme, px, cy);
5883 	if (window_copy_update_selection(wme, 1, 0))
5884 		window_copy_redraw_lines(wme, cy, nd);
5885 }
5886 
5887 /* Scroll down if the cursor went off the visible screen. */
5888 static void
window_copy_acquire_cursor_down(struct window_mode_entry * wme,u_int hsize,u_int sy,u_int oy,u_int oldy,u_int px,u_int py,int no_reset)5889 window_copy_acquire_cursor_down(struct window_mode_entry *wme, u_int hsize,
5890     u_int sy, u_int oy, u_int oldy, u_int px, u_int py, int no_reset)
5891 {
5892 	u_int	cy, yy, ny, nd;
5893 
5894 	cy = py - hsize + oy;
5895 	yy = sy - 1;
5896 	if (cy > yy) {
5897 		ny = cy - yy;
5898 		oldy = yy;
5899 		nd = 1;
5900 	} else {
5901 		ny = 0;
5902 		nd = cy - oldy + 1;
5903 	}
5904 	while (ny > 0) {
5905 	  window_copy_cursor_down(wme, 1);
5906 	  ny--;
5907 	}
5908 	if (cy > yy)
5909 		window_copy_update_cursor(wme, px, yy);
5910 	else
5911 		window_copy_update_cursor(wme, px, cy);
5912 	if (window_copy_update_selection(wme, 1, no_reset))
5913 		window_copy_redraw_lines(wme, oldy, nd);
5914 }
5915