xref: /openbsd-src/usr.bin/tmux/window-copy.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /* $OpenBSD: window-copy.c,v 1.186 2017/11/16 11:16:15 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 <stdlib.h>
23 #include <string.h>
24 
25 #include "tmux.h"
26 
27 static const char *window_copy_key_table(struct window_pane *);
28 static void	window_copy_command(struct window_pane *, struct client *,
29 		    struct session *, struct args *, struct mouse_event *);
30 static struct screen *window_copy_init(struct window_pane *,
31 		    struct cmd_find_state *, struct args *);
32 static void	window_copy_free(struct window_pane *);
33 static int	window_copy_pagedown(struct window_pane *, int, int);
34 static void	window_copy_next_paragraph(struct window_pane *);
35 static void	window_copy_previous_paragraph(struct window_pane *);
36 static void	window_copy_resize(struct window_pane *, u_int, u_int);
37 
38 static void	window_copy_redraw_selection(struct window_pane *, u_int);
39 static void	window_copy_redraw_lines(struct window_pane *, u_int, u_int);
40 static void	window_copy_redraw_screen(struct window_pane *);
41 static void	window_copy_write_line(struct window_pane *,
42 		    struct screen_write_ctx *, u_int);
43 static void	window_copy_write_lines(struct window_pane *,
44 		    struct screen_write_ctx *, u_int, u_int);
45 
46 static void	window_copy_scroll_to(struct window_pane *, u_int, u_int);
47 static int	window_copy_search_compare(struct grid *, u_int, u_int,
48 		    struct grid *, u_int, int);
49 static int	window_copy_search_lr(struct grid *, struct grid *, u_int *,
50 		    u_int, u_int, u_int, int);
51 static int	window_copy_search_rl(struct grid *, struct grid *, u_int *,
52 		    u_int, u_int, u_int, int);
53 static int	window_copy_search_marks(struct window_pane *, struct screen *);
54 static void	window_copy_clear_marks(struct window_pane *);
55 static void	window_copy_move_left(struct screen *, u_int *, u_int *);
56 static void	window_copy_move_right(struct screen *, u_int *, u_int *);
57 static int	window_copy_is_lowercase(const char *);
58 static int	window_copy_search_jump(struct window_pane *, struct grid *,
59 		    struct grid *, u_int, u_int, u_int, int, int, int);
60 static int	window_copy_search(struct window_pane *, int);
61 static int	window_copy_search_up(struct window_pane *);
62 static int	window_copy_search_down(struct window_pane *);
63 static void	window_copy_goto_line(struct window_pane *, const char *);
64 static void	window_copy_update_cursor(struct window_pane *, u_int, u_int);
65 static void	window_copy_start_selection(struct window_pane *);
66 static int	window_copy_adjust_selection(struct window_pane *, u_int *,
67 		    u_int *);
68 static int	window_copy_update_selection(struct window_pane *, int);
69 static void	window_copy_synchronize_cursor(struct window_pane *wp);
70 static void    *window_copy_get_selection(struct window_pane *, size_t *);
71 static void	window_copy_copy_buffer(struct window_pane *, const char *,
72 		    void *, size_t);
73 static void	window_copy_copy_pipe(struct window_pane *, struct session *,
74 		    const char *, const char *);
75 static void	window_copy_copy_selection(struct window_pane *, const char *);
76 static void	window_copy_append_selection(struct window_pane *,
77 		    const char *);
78 static void	window_copy_clear_selection(struct window_pane *);
79 static void	window_copy_copy_line(struct window_pane *, char **, size_t *,
80 		    u_int, u_int, u_int);
81 static int	window_copy_in_set(struct window_pane *, u_int, u_int,
82 		    const char *);
83 static u_int	window_copy_find_length(struct window_pane *, u_int);
84 static void	window_copy_cursor_start_of_line(struct window_pane *);
85 static void	window_copy_cursor_back_to_indentation(struct window_pane *);
86 static void	window_copy_cursor_end_of_line(struct window_pane *);
87 static void	window_copy_other_end(struct window_pane *);
88 static void	window_copy_cursor_left(struct window_pane *);
89 static void	window_copy_cursor_right(struct window_pane *);
90 static void	window_copy_cursor_up(struct window_pane *, int);
91 static void	window_copy_cursor_down(struct window_pane *, int);
92 static void	window_copy_cursor_jump(struct window_pane *);
93 static void	window_copy_cursor_jump_back(struct window_pane *);
94 static void	window_copy_cursor_jump_to(struct window_pane *);
95 static void	window_copy_cursor_jump_to_back(struct window_pane *);
96 static void	window_copy_cursor_next_word(struct window_pane *,
97 		    const char *);
98 static void	window_copy_cursor_next_word_end(struct window_pane *,
99 		    const char *);
100 static void	window_copy_cursor_previous_word(struct window_pane *,
101 		    const char *);
102 static void	window_copy_scroll_up(struct window_pane *, u_int);
103 static void	window_copy_scroll_down(struct window_pane *, u_int);
104 static void	window_copy_rectangle_toggle(struct window_pane *);
105 static void	window_copy_move_mouse(struct mouse_event *);
106 static void	window_copy_drag_update(struct client *, struct mouse_event *);
107 
108 const struct window_mode window_copy_mode = {
109 	.name = "copy-mode",
110 
111 	.init = window_copy_init,
112 	.free = window_copy_free,
113 	.resize = window_copy_resize,
114 	.key_table = window_copy_key_table,
115 	.command = window_copy_command,
116 };
117 
118 enum {
119 	WINDOW_COPY_OFF,
120 	WINDOW_COPY_SEARCHUP,
121 	WINDOW_COPY_SEARCHDOWN,
122 	WINDOW_COPY_JUMPFORWARD,
123 	WINDOW_COPY_JUMPBACKWARD,
124 	WINDOW_COPY_JUMPTOFORWARD,
125 	WINDOW_COPY_JUMPTOBACKWARD,
126 };
127 
128 enum {
129 	WINDOW_COPY_REL_POS_ABOVE,
130 	WINDOW_COPY_REL_POS_ON_SCREEN,
131 	WINDOW_COPY_REL_POS_BELOW,
132 };
133 
134 /*
135  * Copy mode's visible screen (the "screen" field) is filled from one of two
136  * sources: the original contents of the pane (used when we actually enter via
137  * the "copy-mode" command, to copy the contents of the current pane), or else
138  * a series of lines containing the output from an output-writing tmux command
139  * (such as any of the "show-*" or "list-*" commands).
140  *
141  * In either case, the full content of the copy-mode grid is pointed at by the
142  * "backing" field, and is copied into "screen" as needed (that is, when
143  * scrolling occurs). When copy-mode is backed by a pane, backing points
144  * directly at that pane's screen structure (&wp->base); when backed by a list
145  * of output-lines from a command, it points at a newly-allocated screen
146  * structure (which is deallocated when the mode ends).
147  */
148 struct window_copy_mode_data {
149 	struct screen	 screen;
150 
151 	struct screen	*backing;
152 	int		 backing_written; /* backing display started */
153 
154 	u_int		 oy; /* number of lines scrolled up */
155 
156 	u_int		 selx; /* beginning of selection */
157 	u_int		 sely;
158 
159 	u_int		 endselx; /* end of selection */
160 	u_int		 endsely;
161 
162 	enum {
163 		CURSORDRAG_NONE,	/* selection is independent of cursor */
164 		CURSORDRAG_ENDSEL,	/* end is synchronized with cursor */
165 		CURSORDRAG_SEL,		/* start is synchronized with cursor */
166 	} cursordrag;
167 
168 	int		 rectflag;	/* in rectangle copy mode? */
169 	int		 scroll_exit;	/* exit on scroll to end? */
170 
171 	u_int		 cx;
172 	u_int		 cy;
173 
174 	u_int		 lastcx; /* position in last line w/ content */
175 	u_int		 lastsx; /* size of last line w/ content */
176 
177 	int		 searchtype;
178 	char		*searchstr;
179 	bitstr_t        *searchmark;
180 	u_int		 searchcount;
181 	int		 searchthis;
182 	int		 searchx;
183 	int		 searchy;
184 	int		 searcho;
185 
186 	int		 jumptype;
187 	char		 jumpchar;
188 };
189 
190 static struct screen *
191 window_copy_init(struct window_pane *wp, __unused struct cmd_find_state *fs,
192     __unused struct args *args)
193 {
194 	struct window_copy_mode_data	*data;
195 	struct screen			*s;
196 
197 	wp->modedata = data = xmalloc(sizeof *data);
198 
199 	data->oy = 0;
200 	data->cx = 0;
201 	data->cy = 0;
202 
203 	data->cursordrag = CURSORDRAG_NONE;
204 
205 	data->lastcx = 0;
206 	data->lastsx = 0;
207 
208 	data->backing_written = 0;
209 
210 	data->rectflag = 0;
211 	data->scroll_exit = 0;
212 
213 	if (wp->searchstr != NULL) {
214 		data->searchtype = WINDOW_COPY_SEARCHUP;
215 		data->searchstr = xstrdup(wp->searchstr);
216 	} else {
217 		data->searchtype = WINDOW_COPY_OFF;
218 		data->searchstr = NULL;
219 	}
220 	data->searchmark = NULL;
221 	data->searchx = data->searchy = data->searcho = -1;
222 
223 	if (wp->fd != -1)
224 		bufferevent_disable(wp->event, EV_READ|EV_WRITE);
225 
226 	data->jumptype = WINDOW_COPY_OFF;
227 	data->jumpchar = '\0';
228 
229 	s = &data->screen;
230 	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
231 	s->sel.modekeys = options_get_number(wp->window->options, "mode-keys");
232 
233 	data->backing = NULL;
234 
235 	return (s);
236 }
237 
238 void
239 window_copy_init_from_pane(struct window_pane *wp, int scroll_exit)
240 {
241 	struct window_copy_mode_data	*data = wp->modedata;
242 	struct screen			*s = &data->screen;
243 	struct screen_write_ctx	 	 ctx;
244 	u_int				 i;
245 
246 	if (wp->mode != &window_copy_mode)
247 		fatalx("not in copy mode");
248 
249 	data->backing = &wp->base;
250 	data->cx = data->backing->cx;
251 	data->cy = data->backing->cy;
252 	data->scroll_exit = scroll_exit;
253 
254 	s->cx = data->cx;
255 	s->cy = data->cy;
256 
257 	screen_write_start(&ctx, NULL, s);
258 	for (i = 0; i < screen_size_y(s); i++)
259 		window_copy_write_line(wp, &ctx, i);
260 	screen_write_cursormove(&ctx, data->cx, data->cy);
261 	screen_write_stop(&ctx);
262 }
263 
264 void
265 window_copy_init_for_output(struct window_pane *wp)
266 {
267 	struct window_copy_mode_data	*data = wp->modedata;
268 
269 	data->backing = xmalloc(sizeof *data->backing);
270 	screen_init(data->backing, screen_size_x(&wp->base),
271 	    screen_size_y(&wp->base), UINT_MAX);
272 }
273 
274 static void
275 window_copy_free(struct window_pane *wp)
276 {
277 	struct window_copy_mode_data	*data = wp->modedata;
278 
279 	if (wp->fd != -1)
280 		bufferevent_enable(wp->event, EV_READ|EV_WRITE);
281 
282 	free(data->searchmark);
283 	free(data->searchstr);
284 
285 	if (data->backing != &wp->base) {
286 		screen_free(data->backing);
287 		free(data->backing);
288 	}
289 	screen_free(&data->screen);
290 
291 	free(data);
292 }
293 
294 void
295 window_copy_add(struct window_pane *wp, const char *fmt, ...)
296 {
297 	va_list	ap;
298 
299 	va_start(ap, fmt);
300 	window_copy_vadd(wp, fmt, ap);
301 	va_end(ap);
302 }
303 
304 void
305 window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
306 {
307 	struct window_copy_mode_data	*data = wp->modedata;
308 	struct screen			*backing = data->backing;
309 	struct screen_write_ctx	 	 back_ctx, ctx;
310 	struct grid_cell		 gc;
311 	u_int				 old_hsize, old_cy;
312 
313 	if (backing == &wp->base)
314 		return;
315 
316 	memcpy(&gc, &grid_default_cell, sizeof gc);
317 
318 	old_hsize = screen_hsize(data->backing);
319 	screen_write_start(&back_ctx, NULL, backing);
320 	if (data->backing_written) {
321 		/*
322 		 * On the second or later line, do a CRLF before writing
323 		 * (so it's on a new line).
324 		 */
325 		screen_write_carriagereturn(&back_ctx);
326 		screen_write_linefeed(&back_ctx, 0, 8);
327 	} else
328 		data->backing_written = 1;
329 	old_cy = backing->cy;
330 	screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap);
331 	screen_write_stop(&back_ctx);
332 
333 	data->oy += screen_hsize(data->backing) - old_hsize;
334 
335 	screen_write_start(&ctx, wp, &data->screen);
336 
337 	/*
338 	 * If the history has changed, draw the top line.
339 	 * (If there's any history at all, it has changed.)
340 	 */
341 	if (screen_hsize(data->backing))
342 		window_copy_redraw_lines(wp, 0, 1);
343 
344 	/* Write the new lines. */
345 	window_copy_redraw_lines(wp, old_cy, backing->cy - old_cy + 1);
346 
347 	screen_write_stop(&ctx);
348 }
349 
350 void
351 window_copy_pageup(struct window_pane *wp, int half_page)
352 {
353 	struct window_copy_mode_data	*data = wp->modedata;
354 	struct screen			*s = &data->screen;
355 	u_int				 n, ox, oy, px, py;
356 
357 	oy = screen_hsize(data->backing) + data->cy - data->oy;
358 	ox = window_copy_find_length(wp, oy);
359 
360 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
361 		window_copy_other_end(wp);
362 
363 	if (data->cx != ox) {
364 		data->lastcx = data->cx;
365 		data->lastsx = ox;
366 	}
367 	data->cx = data->lastcx;
368 
369 	n = 1;
370 	if (screen_size_y(s) > 2) {
371 		if (half_page)
372 			n = screen_size_y(s) / 2;
373 		else
374 			n = screen_size_y(s) - 2;
375 	}
376 
377 	if (data->oy + n > screen_hsize(data->backing))
378 		data->oy = screen_hsize(data->backing);
379 	else
380 		data->oy += n;
381 
382 	if (!data->screen.sel.flag || !data->rectflag) {
383 		py = screen_hsize(data->backing) + data->cy - data->oy;
384 		px = window_copy_find_length(wp, py);
385 		if ((data->cx >= data->lastsx && data->cx != px) ||
386 		    data->cx > px)
387 			window_copy_cursor_end_of_line(wp);
388 	}
389 
390 	window_copy_update_selection(wp, 1);
391 	window_copy_redraw_screen(wp);
392 }
393 
394 static int
395 window_copy_pagedown(struct window_pane *wp, int half_page, int scroll_exit)
396 {
397 	struct window_copy_mode_data	*data = wp->modedata;
398 	struct screen			*s = &data->screen;
399 	u_int				 n, ox, oy, px, py;
400 
401 	oy = screen_hsize(data->backing) + data->cy - data->oy;
402 	ox = window_copy_find_length(wp, oy);
403 
404 	if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely)
405 		window_copy_other_end(wp);
406 
407 	if (data->cx != ox) {
408 		data->lastcx = data->cx;
409 		data->lastsx = ox;
410 	}
411 	data->cx = data->lastcx;
412 
413 	n = 1;
414 	if (screen_size_y(s) > 2) {
415 		if (half_page)
416 			n = screen_size_y(s) / 2;
417 		else
418 			n = screen_size_y(s) - 2;
419 	}
420 
421 	if (data->oy < n)
422 		data->oy = 0;
423 	else
424 		data->oy -= n;
425 
426 	if (!data->screen.sel.flag || !data->rectflag) {
427 		py = screen_hsize(data->backing) + data->cy - data->oy;
428 		px = window_copy_find_length(wp, py);
429 		if ((data->cx >= data->lastsx && data->cx != px) ||
430 		    data->cx > px)
431 			window_copy_cursor_end_of_line(wp);
432 	}
433 
434 	if (scroll_exit && data->oy == 0)
435 		return (1);
436 	window_copy_update_selection(wp, 1);
437 	window_copy_redraw_screen(wp);
438 	return (0);
439 }
440 
441 static void
442 window_copy_previous_paragraph(struct window_pane *wp)
443 {
444 	struct window_copy_mode_data	*data = wp->modedata;
445 	u_int				 oy;
446 
447 	oy = screen_hsize(data->backing) + data->cy - data->oy;
448 
449 	while (oy > 0 && window_copy_find_length(wp, oy) == 0)
450 		oy--;
451 
452 	while (oy > 0 && window_copy_find_length(wp, oy) > 0)
453 		oy--;
454 
455 	window_copy_scroll_to(wp, 0, oy);
456 }
457 
458 static void
459 window_copy_next_paragraph(struct window_pane *wp)
460 {
461 	struct window_copy_mode_data	*data = wp->modedata;
462 	struct screen			*s = &data->screen;
463 	u_int				 maxy, ox, oy;
464 
465 	oy = screen_hsize(data->backing) + data->cy - data->oy;
466 	maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
467 
468 	while (oy < maxy && window_copy_find_length(wp, oy) == 0)
469 		oy++;
470 
471 	while (oy < maxy && window_copy_find_length(wp, oy) > 0)
472 		oy++;
473 
474 	ox = window_copy_find_length(wp, oy);
475 	window_copy_scroll_to(wp, ox, oy);
476 }
477 
478 static void
479 window_copy_resize(struct window_pane *wp, u_int sx, u_int sy)
480 {
481 	struct window_copy_mode_data	*data = wp->modedata;
482 	struct screen			*s = &data->screen;
483 	struct screen_write_ctx	 	 ctx;
484 
485 	screen_resize(s, sx, sy, 1);
486 	if (data->backing != &wp->base)
487 		screen_resize(data->backing, sx, sy, 1);
488 
489 	if (data->cy > sy - 1)
490 		data->cy = sy - 1;
491 	if (data->cx > sx)
492 		data->cx = sx;
493 	if (data->oy > screen_hsize(data->backing))
494 		data->oy = screen_hsize(data->backing);
495 
496 	window_copy_clear_selection(wp);
497 
498 	screen_write_start(&ctx, NULL, s);
499 	window_copy_write_lines(wp, &ctx, 0, screen_size_y(s) - 1);
500 	screen_write_stop(&ctx);
501 
502 	if (data->searchmark != NULL)
503 		window_copy_search_marks(wp, NULL);
504 	data->searchx = data->cx;
505 	data->searchy = data->cy;
506 	data->searcho = data->oy;
507 
508 	window_copy_redraw_screen(wp);
509 }
510 
511 static const char *
512 window_copy_key_table(struct window_pane *wp)
513 {
514 	if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
515 		return ("copy-mode-vi");
516 	return ("copy-mode");
517 }
518 
519 static void
520 window_copy_command(struct window_pane *wp, struct client *c, struct session *s,
521     struct args *args, struct mouse_event *m)
522 {
523 	struct window_copy_mode_data	*data = wp->modedata;
524 	struct screen			*sn = &data->screen;
525 	const char			*command, *argument, *ws;
526 	u_int				 np = wp->modeprefix;
527 	int				 cancel = 0, redraw = 0, scroll_exit;
528 	char				 prefix;
529 
530 	if (args->argc == 0)
531 		return;
532 	command = args->argv[0];
533 
534 	if (m != NULL && m->valid)
535 		window_copy_move_mouse(m);
536 
537 	if (args->argc == 1) {
538 		if (strcmp(command, "append-selection") == 0) {
539 			if (s != NULL)
540 				window_copy_append_selection(wp, NULL);
541 			window_copy_clear_selection(wp);
542 			redraw = 1;
543 		}
544 		if (strcmp(command, "append-selection-and-cancel") == 0) {
545 			if (s != NULL)
546 				window_copy_append_selection(wp, NULL);
547 			window_copy_clear_selection(wp);
548 			redraw = 1;
549 			cancel = 1;
550 		}
551 		if (strcmp(command, "back-to-indentation") == 0)
552 			window_copy_cursor_back_to_indentation(wp);
553 		if (strcmp(command, "begin-selection") == 0) {
554 			if (m != NULL)
555 				window_copy_start_drag(c, m);
556 			else {
557 				sn->sel.lineflag = LINE_SEL_NONE;
558 				window_copy_start_selection(wp);
559 				redraw = 1;
560 			}
561 		}
562 		if (strcmp(command, "stop-selection") == 0)
563 			data->cursordrag = CURSORDRAG_NONE;
564 		if (strcmp(command, "bottom-line") == 0) {
565 			data->cx = 0;
566 			data->cy = screen_size_y(sn) - 1;
567 			window_copy_update_selection(wp, 1);
568 			redraw = 1;
569 		}
570 		if (strcmp(command, "cancel") == 0)
571 			cancel = 1;
572 		if (strcmp(command, "clear-selection") == 0) {
573 			window_copy_clear_selection(wp);
574 			redraw = 1;
575 		}
576 		if (strcmp(command, "copy-end-of-line") == 0) {
577 			window_copy_start_selection(wp);
578 			for (; np > 1; np--)
579 				window_copy_cursor_down(wp, 0);
580 			window_copy_cursor_end_of_line(wp);
581 			redraw = 1;
582 
583 			if (s != NULL) {
584 				window_copy_copy_selection(wp, NULL);
585 				cancel = 1;
586 			}
587 		}
588 		if (strcmp(command, "copy-line") == 0) {
589 			window_copy_cursor_start_of_line(wp);
590 			window_copy_start_selection(wp);
591 			for (; np > 1; np--)
592 				window_copy_cursor_down(wp, 0);
593 			window_copy_cursor_end_of_line(wp);
594 			redraw = 1;
595 
596 			if (s != NULL) {
597 				window_copy_copy_selection(wp, NULL);
598 				cancel = 1;
599 			}
600 		}
601 		if (strcmp(command, "copy-selection") == 0) {
602 			if (s != NULL)
603 				window_copy_copy_selection(wp, NULL);
604 			window_copy_clear_selection(wp);
605 			redraw = 1;
606 		}
607 		if (strcmp(command, "copy-selection-and-cancel") == 0) {
608 			if (s != NULL)
609 				window_copy_copy_selection(wp, NULL);
610 			window_copy_clear_selection(wp);
611 			redraw = 1;
612 			cancel = 1;
613 		}
614 		if (strcmp(command, "cursor-down") == 0) {
615 			for (; np != 0; np--)
616 				window_copy_cursor_down(wp, 0);
617 		}
618 		if (strcmp(command, "cursor-left") == 0) {
619 			for (; np != 0; np--)
620 				window_copy_cursor_left(wp);
621 		}
622 		if (strcmp(command, "cursor-right") == 0) {
623 			for (; np != 0; np--)
624 				window_copy_cursor_right(wp);
625 		}
626 		if (strcmp(command, "cursor-up") == 0) {
627 			for (; np != 0; np--)
628 				window_copy_cursor_up(wp, 0);
629 		}
630 		if (strcmp(command, "end-of-line") == 0)
631 			window_copy_cursor_end_of_line(wp);
632 		if (strcmp(command, "halfpage-down") == 0 ||
633 		    strcmp(command, "halfpage-down-and-cancel") == 0) {
634 			if (strcmp(command, "halfpage-down-and-cancel") == 0)
635 				scroll_exit = 1;
636 			else
637 				scroll_exit = data->scroll_exit;
638 			for (; np != 0; np--) {
639 				if (window_copy_pagedown(wp, 1, scroll_exit)) {
640 					cancel = 1;
641 					break;
642 				}
643 			}
644 		}
645 		if (strcmp(command, "halfpage-up") == 0) {
646 			for (; np != 0; np--)
647 				window_copy_pageup(wp, 1);
648 		}
649 		if (strcmp(command, "history-bottom") == 0) {
650 			data->cx = 0;
651 			data->cy = screen_size_y(sn) - 1;
652 			data->oy = 0;
653 			window_copy_update_selection(wp, 1);
654 			redraw = 1;
655 		}
656 		if (strcmp(command, "history-top") == 0) {
657 			data->cx = 0;
658 			data->cy = 0;
659 			data->oy = screen_hsize(data->backing);
660 			window_copy_update_selection(wp, 1);
661 			redraw = 1;
662 		}
663 		if (strcmp(command, "jump-again") == 0) {
664 			switch (data->jumptype) {
665 			case WINDOW_COPY_JUMPFORWARD:
666 				for (; np != 0; np--)
667 					window_copy_cursor_jump(wp);
668 				break;
669 			case WINDOW_COPY_JUMPBACKWARD:
670 				for (; np != 0; np--)
671 					window_copy_cursor_jump_back(wp);
672 				break;
673 			case WINDOW_COPY_JUMPTOFORWARD:
674 				for (; np != 0; np--)
675 					window_copy_cursor_jump_to(wp);
676 				break;
677 			case WINDOW_COPY_JUMPTOBACKWARD:
678 				for (; np != 0; np--)
679 					window_copy_cursor_jump_to_back(wp);
680 				break;
681 			}
682 		}
683 		if (strcmp(command, "jump-reverse") == 0) {
684 			switch (data->jumptype) {
685 			case WINDOW_COPY_JUMPFORWARD:
686 				for (; np != 0; np--)
687 					window_copy_cursor_jump_back(wp);
688 				break;
689 			case WINDOW_COPY_JUMPBACKWARD:
690 				for (; np != 0; np--)
691 					window_copy_cursor_jump(wp);
692 				break;
693 			case WINDOW_COPY_JUMPTOFORWARD:
694 				for (; np != 0; np--)
695 					window_copy_cursor_jump_to_back(wp);
696 				break;
697 			case WINDOW_COPY_JUMPTOBACKWARD:
698 				for (; np != 0; np--)
699 					window_copy_cursor_jump_to(wp);
700 				break;
701 			}
702 		}
703 		if (strcmp(command, "middle-line") == 0) {
704 			data->cx = 0;
705 			data->cy = (screen_size_y(sn) - 1) / 2;
706 			window_copy_update_selection(wp, 1);
707 			redraw = 1;
708 		}
709 		if (strcmp(command, "next-paragraph") == 0) {
710 			for (; np != 0; np--)
711 				window_copy_next_paragraph(wp);
712 		}
713 		if (strcmp(command, "next-space") == 0) {
714 			for (; np != 0; np--)
715 				window_copy_cursor_next_word(wp, " ");
716 		}
717 		if (strcmp(command, "next-space-end") == 0) {
718 			for (; np != 0; np--)
719 				window_copy_cursor_next_word_end(wp, " ");
720 		}
721 		if (strcmp(command, "next-word") == 0) {
722 			ws = options_get_string(s->options, "word-separators");
723 			for (; np != 0; np--)
724 				window_copy_cursor_next_word(wp, ws);
725 		}
726 		if (strcmp(command, "next-word-end") == 0) {
727 			ws = options_get_string(s->options, "word-separators");
728 			for (; np != 0; np--)
729 				window_copy_cursor_next_word_end(wp, ws);
730 		}
731 		if (strcmp(command, "other-end") == 0) {
732 			if ((np % 2) != 0)
733 				window_copy_other_end(wp);
734 		}
735 		if (strcmp(command, "page-down") == 0 ||
736 		    strcmp(command, "page-down-and-cancel") == 0) {
737 			if (strcmp(command, "page-down-and-cancel") == 0)
738 				scroll_exit = 1;
739 			else
740 				scroll_exit = data->scroll_exit;
741 			for (; np != 0; np--) {
742 				if (window_copy_pagedown(wp, 0, scroll_exit)) {
743 					cancel = 1;
744 					break;
745 				}
746 			}
747 		}
748 		if (strcmp(command, "page-up") == 0) {
749 			for (; np != 0; np--)
750 				window_copy_pageup(wp, 0);
751 		}
752 		if (strcmp(command, "previous-paragraph") == 0) {
753 			for (; np != 0; np--)
754 				window_copy_previous_paragraph(wp);
755 		}
756 		if (strcmp(command, "previous-space") == 0) {
757 			for (; np != 0; np--)
758 				window_copy_cursor_previous_word(wp, " ");
759 		}
760 		if (strcmp(command, "previous-word") == 0) {
761 			ws = options_get_string(s->options, "word-separators");
762 			for (; np != 0; np--)
763 				window_copy_cursor_previous_word(wp, ws);
764 		}
765 		if (strcmp(command, "rectangle-toggle") == 0) {
766 			sn->sel.lineflag = LINE_SEL_NONE;
767 			window_copy_rectangle_toggle(wp);
768 		}
769 		if (strcmp(command, "scroll-down") == 0 ||
770 		    strcmp(command, "scroll-down-and-cancel") == 0) {
771 			if (strcmp(command, "scroll-down-and-cancel") == 0)
772 				scroll_exit = 1;
773 			else
774 				scroll_exit = data->scroll_exit;
775 			for (; np != 0; np--)
776 				window_copy_cursor_down(wp, 1);
777 			if (scroll_exit && data->oy == 0)
778 				cancel = 1;
779 		}
780 		if (strcmp(command, "scroll-up") == 0) {
781 			for (; np != 0; np--)
782 				window_copy_cursor_up(wp, 1);
783 		}
784 		if (strcmp(command, "search-again") == 0) {
785 			if (data->searchtype == WINDOW_COPY_SEARCHUP) {
786 				for (; np != 0; np--)
787 					window_copy_search_up(wp);
788 			} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
789 				for (; np != 0; np--)
790 					window_copy_search_down(wp);
791 			}
792 		}
793 		if (strcmp(command, "search-reverse") == 0) {
794 			if (data->searchtype == WINDOW_COPY_SEARCHUP) {
795 				for (; np != 0; np--)
796 					window_copy_search_down(wp);
797 			} else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
798 				for (; np != 0; np--)
799 					window_copy_search_up(wp);
800 			}
801 		}
802 		if (strcmp(command, "select-line") == 0) {
803 			sn->sel.lineflag = LINE_SEL_LEFT_RIGHT;
804 			data->rectflag = 0;
805 			window_copy_cursor_start_of_line(wp);
806 			window_copy_start_selection(wp);
807 			for (; np > 1; np--)
808 				window_copy_cursor_down(wp, 0);
809 			window_copy_cursor_end_of_line(wp);
810 			redraw = 1;
811 		}
812 		if (strcmp(command, "select-word") == 0) {
813 			sn->sel.lineflag = LINE_SEL_LEFT_RIGHT;
814 			data->rectflag = 0;
815 			ws = options_get_string(s->options, "word-separators");
816 			window_copy_cursor_previous_word(wp, ws);
817 			window_copy_start_selection(wp);
818 			window_copy_cursor_next_word_end(wp, ws);
819 			redraw = 1;
820 		}
821 		if (strcmp(command, "start-of-line") == 0)
822 			window_copy_cursor_start_of_line(wp);
823 		if (strcmp(command, "top-line") == 0) {
824 			data->cx = 0;
825 			data->cy = 0;
826 			window_copy_update_selection(wp, 1);
827 			redraw = 1;
828 		}
829 	} else if (args->argc == 2 && *args->argv[1] != '\0') {
830 		argument = args->argv[1];
831 		if (strcmp(command, "copy-pipe") == 0) {
832 			if (s != NULL)
833 				window_copy_copy_pipe(wp, s, NULL, argument);
834 		}
835 		if (strcmp(command, "copy-pipe-and-cancel") == 0) {
836 			if (s != NULL) {
837 				window_copy_copy_pipe(wp, s, NULL, argument);
838 				cancel = 1;
839 			}
840 		}
841 		if (strcmp(command, "goto-line") == 0)
842 			window_copy_goto_line(wp, argument);
843 		if (strcmp(command, "jump-backward") == 0) {
844 			data->jumptype = WINDOW_COPY_JUMPBACKWARD;
845 			data->jumpchar = *argument;
846 			for (; np != 0; np--)
847 				window_copy_cursor_jump_back(wp);
848 		}
849 		if (strcmp(command, "jump-forward") == 0) {
850 			data->jumptype = WINDOW_COPY_JUMPFORWARD;
851 			data->jumpchar = *argument;
852 			for (; np != 0; np--)
853 				window_copy_cursor_jump(wp);
854 		}
855 		if (strcmp(command, "jump-to-backward") == 0) {
856 			data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
857 			data->jumpchar = *argument;
858 			for (; np != 0; np--)
859 				window_copy_cursor_jump_to_back(wp);
860 		}
861 		if (strcmp(command, "jump-to-forward") == 0) {
862 			data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
863 			data->jumpchar = *argument;
864 			for (; np != 0; np--)
865 				window_copy_cursor_jump_to(wp);
866 		}
867 		if (strcmp(command, "search-backward") == 0) {
868 			data->searchtype = WINDOW_COPY_SEARCHUP;
869 			free(data->searchstr);
870 			data->searchstr = xstrdup(argument);
871 			for (; np != 0; np--)
872 				window_copy_search_up(wp);
873 		}
874 		if (strcmp(command, "search-forward") == 0) {
875 			data->searchtype = WINDOW_COPY_SEARCHDOWN;
876 			free(data->searchstr);
877 			data->searchstr = xstrdup(argument);
878 			for (; np != 0; np--)
879 				window_copy_search_down(wp);
880 		}
881 		if (strcmp(command, "search-backward-incremental") == 0) {
882 			prefix = *argument++;
883 			if (data->searchx == -1 || data->searchy == -1) {
884 				data->searchx = data->cx;
885 				data->searchy = data->cy;
886 				data->searcho = data->oy;
887 			} else if (data->searchstr != NULL &&
888 			    strcmp(argument, data->searchstr) != 0) {
889 				data->cx = data->searchx;
890 				data->cy = data->searchy;
891 				data->oy = data->searcho;
892 				redraw = 1;
893 			}
894 			if (*argument == '\0') {
895 				window_copy_clear_marks(wp);
896 				redraw = 1;
897 			} else if (prefix == '=' || prefix == '-') {
898 				data->searchtype = WINDOW_COPY_SEARCHUP;
899 				free(data->searchstr);
900 				data->searchstr = xstrdup(argument);
901 				if (!window_copy_search_up(wp)) {
902 					window_copy_clear_marks(wp);
903 					redraw = 1;
904 				}
905 			} else if (prefix == '+') {
906 				data->searchtype = WINDOW_COPY_SEARCHDOWN;
907 				free(data->searchstr);
908 				data->searchstr = xstrdup(argument);
909 				if (!window_copy_search_down(wp)) {
910 					window_copy_clear_marks(wp);
911 					redraw = 1;
912 				}
913 			}
914 		}
915 		if (strcmp(command, "search-forward-incremental") == 0) {
916 			prefix = *argument++;
917 			if (data->searchx == -1 || data->searchy == -1) {
918 				data->searchx = data->cx;
919 				data->searchy = data->cy;
920 				data->searcho = data->oy;
921 			} else if (data->searchstr != NULL &&
922 			    strcmp(argument, data->searchstr) != 0) {
923 				data->cx = data->searchx;
924 				data->cy = data->searchy;
925 				data->oy = data->searcho;
926 				redraw = 1;
927 			}
928 			if (*argument == '\0') {
929 				window_copy_clear_marks(wp);
930 				redraw = 1;
931 			} else if (prefix == '=' || prefix == '+') {
932 				data->searchtype = WINDOW_COPY_SEARCHDOWN;
933 				free(data->searchstr);
934 				data->searchstr = xstrdup(argument);
935 				if (!window_copy_search_down(wp)) {
936 					window_copy_clear_marks(wp);
937 					redraw = 1;
938 				}
939 			} else if (prefix == '-') {
940 				data->searchtype = WINDOW_COPY_SEARCHUP;
941 				free(data->searchstr);
942 				data->searchstr = xstrdup(argument);
943 				if (!window_copy_search_up(wp)) {
944 					window_copy_clear_marks(wp);
945 					redraw = 1;
946 				}
947 			}
948 		}
949 	}
950 
951 	if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
952 		window_copy_clear_marks(wp);
953 		redraw = 1;
954 		data->searchx = data->searchy = -1;
955 	}
956 
957 	if (cancel)
958 		window_pane_reset_mode(wp);
959 	else if (redraw)
960 		window_copy_redraw_screen(wp);
961 	wp->modeprefix = 1;
962 }
963 
964 static void
965 window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
966 {
967 	struct window_copy_mode_data	*data = wp->modedata;
968 	struct grid			*gd = data->backing->grid;
969 	u_int				 offset, gap;
970 
971 	data->cx = px;
972 
973 	if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy)
974 		data->cy = py - (gd->hsize - data->oy);
975 	else {
976 		gap = gd->sy / 4;
977 		if (py < gd->sy) {
978 			offset = 0;
979 			data->cy = py;
980 		} else if (py > gd->hsize + gd->sy - gap) {
981 			offset = gd->hsize;
982 			data->cy = py - gd->hsize;
983 		} else {
984 			offset = py + gap - gd->sy;
985 			data->cy = py - offset;
986 		}
987 		data->oy = gd->hsize - offset;
988 	}
989 
990 	window_copy_update_selection(wp, 1);
991 	window_copy_redraw_screen(wp);
992 }
993 
994 static int
995 window_copy_search_compare(struct grid *gd, u_int px, u_int py,
996     struct grid *sgd, u_int spx, int cis)
997 {
998 	struct grid_cell	 gc, sgc;
999 	const struct utf8_data	*ud, *sud;
1000 
1001 	grid_get_cell(gd, px, py, &gc);
1002 	ud = &gc.data;
1003 	grid_get_cell(sgd, spx, 0, &sgc);
1004 	sud = &sgc.data;
1005 
1006 	if (ud->size != sud->size || ud->width != sud->width)
1007 		return (0);
1008 
1009 	if (cis && ud->size == 1)
1010 		return (tolower(ud->data[0]) == sud->data[0]);
1011 
1012 	return (memcmp(ud->data, sud->data, ud->size) == 0);
1013 }
1014 
1015 static int
1016 window_copy_search_lr(struct grid *gd,
1017     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
1018 {
1019 	u_int	ax, bx, px;
1020 	int	matched;
1021 
1022 	for (ax = first; ax < last; ax++) {
1023 		if (ax + sgd->sx > gd->sx)
1024 			break;
1025 		for (bx = 0; bx < sgd->sx; bx++) {
1026 			px = ax + bx;
1027 			matched = window_copy_search_compare(gd, px, py, sgd,
1028 			    bx, cis);
1029 			if (!matched)
1030 				break;
1031 		}
1032 		if (bx == sgd->sx) {
1033 			*ppx = ax;
1034 			return (1);
1035 		}
1036 	}
1037 	return (0);
1038 }
1039 
1040 static int
1041 window_copy_search_rl(struct grid *gd,
1042     struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
1043 {
1044 	u_int	ax, bx, px;
1045 	int	matched;
1046 
1047 	for (ax = last + 1; ax > first; ax--) {
1048 		if (gd->sx - (ax - 1) < sgd->sx)
1049 			continue;
1050 		for (bx = 0; bx < sgd->sx; bx++) {
1051 			px = ax - 1 + bx;
1052 			matched = window_copy_search_compare(gd, px, py, sgd,
1053 			    bx, cis);
1054 			if (!matched)
1055 				break;
1056 		}
1057 		if (bx == sgd->sx) {
1058 			*ppx = ax - 1;
1059 			return (1);
1060 		}
1061 	}
1062 	return (0);
1063 }
1064 
1065 static void
1066 window_copy_move_left(struct screen *s, u_int *fx, u_int *fy)
1067 {
1068 	if (*fx == 0) {	/* left */
1069 		if (*fy == 0) /* top */
1070 			return;
1071 		*fx = screen_size_x(s) - 1;
1072 		*fy = *fy - 1;
1073 	} else
1074 		*fx = *fx - 1;
1075 }
1076 
1077 static void
1078 window_copy_move_right(struct screen *s, u_int *fx, u_int *fy)
1079 {
1080 	if (*fx == screen_size_x(s) - 1) { /* right */
1081 		if (*fy == screen_hsize(s) + screen_size_y(s)) /* bottom */
1082 			return;
1083 		*fx = 0;
1084 		*fy = *fy + 1;
1085 	} else
1086 		*fx = *fx + 1;
1087 }
1088 
1089 static int
1090 window_copy_is_lowercase(const char *ptr)
1091 {
1092 	while (*ptr != '\0') {
1093 		if (*ptr != tolower((u_char)*ptr))
1094 			return (0);
1095 		++ptr;
1096 	}
1097 	return (1);
1098 }
1099 
1100 /*
1101  * Search for text stored in sgd starting from position fx,fy up to endline. If
1102  * found, jump to it. If cis then ignore case. The direction is 0 for searching
1103  * up, down otherwise. If wrap then go to begin/end of grid and try again if
1104  * not found.
1105  */
1106 static int
1107 window_copy_search_jump(struct window_pane *wp, struct grid *gd,
1108     struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
1109     int direction)
1110 {
1111 	u_int	i, px;
1112 	int	found;
1113 
1114 	found = 0;
1115 	if (direction) {
1116 		for (i = fy; i <= endline; i++) {
1117 			found = window_copy_search_lr(gd, sgd, &px, i, fx,
1118 			    gd->sx, cis);
1119 			if (found)
1120 				break;
1121 			fx = 0;
1122 		}
1123 	} else {
1124 		for (i = fy + 1; endline < i; i--) {
1125 			found = window_copy_search_rl(gd, sgd, &px, i - 1, 0,
1126 			    fx, cis);
1127 			if (found) {
1128 				i--;
1129 				break;
1130 			}
1131 			fx = gd->sx;
1132 		}
1133 	}
1134 
1135 	if (found) {
1136 		window_copy_scroll_to(wp, px, i);
1137 		return (1);
1138 	}
1139 	if (wrap) {
1140 		return (window_copy_search_jump(wp, gd, sgd,
1141 		    direction ? 0 : gd->sx - 1,
1142 		    direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
1143 		    direction));
1144 	}
1145 	return (0);
1146 }
1147 
1148 /*
1149  * Search in for text searchstr. If direction is 0 then search up, otherwise
1150  * down.
1151  */
1152 static int
1153 window_copy_search(struct window_pane *wp, int direction)
1154 {
1155 	struct window_copy_mode_data	*data = wp->modedata;
1156 	struct screen			*s = data->backing, ss;
1157 	struct screen_write_ctx		 ctx;
1158 	struct grid			*gd = s->grid;
1159 	u_int				 fx, fy, endline;
1160 	int				 wrapflag, cis, found;
1161 
1162 	free(wp->searchstr);
1163 	wp->searchstr = xstrdup(data->searchstr);
1164 
1165 	fx = data->cx;
1166 	fy = screen_hsize(data->backing) - data->oy + data->cy;
1167 
1168 	screen_init(&ss, screen_write_strlen("%s", data->searchstr), 1, 0);
1169 	screen_write_start(&ctx, NULL, &ss);
1170 	screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", data->searchstr);
1171 	screen_write_stop(&ctx);
1172 
1173 	if (direction)
1174 		window_copy_move_right(s, &fx, &fy);
1175 	else
1176 		window_copy_move_left(s, &fx, &fy);
1177 	window_copy_clear_selection(wp);
1178 
1179 	wrapflag = options_get_number(wp->window->options, "wrap-search");
1180 	cis = window_copy_is_lowercase(data->searchstr);
1181 
1182 	if (direction)
1183 		endline = gd->hsize + gd->sy - 1;
1184 	else
1185 		endline = 0;
1186 	found = window_copy_search_jump(wp, gd, ss.grid, fx, fy, endline, cis,
1187 	    wrapflag, direction);
1188 
1189 	if (window_copy_search_marks(wp, &ss))
1190 		window_copy_redraw_screen(wp);
1191 
1192 	screen_free(&ss);
1193 	return (found);
1194 }
1195 
1196 static int
1197 window_copy_search_marks(struct window_pane *wp, struct screen *ssp)
1198 {
1199 	struct window_copy_mode_data	*data = wp->modedata;
1200 	struct screen			*s = data->backing, ss;
1201 	struct screen_write_ctx		 ctx;
1202 	struct grid			*gd = s->grid;
1203 	int				 found, cis, which = -1;
1204 	u_int				 px, py, b, nfound = 0, width;
1205 
1206 	if (ssp == NULL) {
1207 		width = screen_write_strlen("%s", data->searchstr);
1208 		screen_init(&ss, width, 1, 0);
1209 		screen_write_start(&ctx, NULL, &ss);
1210 		screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
1211 		    data->searchstr);
1212 		screen_write_stop(&ctx);
1213 		ssp = &ss;
1214 	} else
1215 		width = screen_size_x(ssp);
1216 
1217 	cis = window_copy_is_lowercase(data->searchstr);
1218 
1219 	free(data->searchmark);
1220 	data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx);
1221 
1222 	for (py = 0; py < gd->hsize + gd->sy; py++) {
1223 		px = 0;
1224 		for (;;) {
1225 			found = window_copy_search_lr(gd, ssp->grid, &px, py,
1226 			    px, gd->sx, cis);
1227 			if (!found)
1228 				break;
1229 
1230 			nfound++;
1231 			if (px == data->cx && py == gd->hsize + data->cy - data->oy)
1232 				which = nfound;
1233 
1234 			b = (py * gd->sx) + px;
1235 			bit_nset(data->searchmark, b, b + width - 1);
1236 
1237 			px++;
1238 		}
1239 	}
1240 
1241 	if (which != -1)
1242 		data->searchthis = 1 + nfound - which;
1243 	else
1244 		data->searchthis = -1;
1245 	data->searchcount = nfound;
1246 
1247 	if (ssp == &ss)
1248 		screen_free(&ss);
1249 	return (nfound);
1250 }
1251 
1252 static void
1253 window_copy_clear_marks(struct window_pane *wp)
1254 {
1255 	struct window_copy_mode_data	*data = wp->modedata;
1256 
1257 	free(data->searchmark);
1258 	data->searchmark = NULL;
1259 }
1260 
1261 static int
1262 window_copy_search_up(struct window_pane *wp)
1263 {
1264 	return (window_copy_search(wp, 0));
1265 }
1266 
1267 static int
1268 window_copy_search_down(struct window_pane *wp)
1269 {
1270 	return (window_copy_search(wp, 1));
1271 }
1272 
1273 static void
1274 window_copy_goto_line(struct window_pane *wp, const char *linestr)
1275 {
1276 	struct window_copy_mode_data	*data = wp->modedata;
1277 	const char			*errstr;
1278 	u_int				 lineno;
1279 
1280 	lineno = strtonum(linestr, 0, screen_hsize(data->backing), &errstr);
1281 	if (errstr != NULL)
1282 		return;
1283 
1284 	data->oy = lineno;
1285 	window_copy_update_selection(wp, 1);
1286 	window_copy_redraw_screen(wp);
1287 }
1288 
1289 static void
1290 window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
1291     u_int py)
1292 {
1293 	struct window_copy_mode_data	*data = wp->modedata;
1294 	struct screen			*s = &data->screen;
1295 	struct options			*oo = wp->window->options;
1296 	struct grid_cell		 gc;
1297 	char				 hdr[512];
1298 	size_t				 size = 0;
1299 
1300 	style_apply(&gc, oo, "mode-style");
1301 	gc.flags |= GRID_FLAG_NOPALETTE;
1302 
1303 	if (py == 0) {
1304 		if (data->searchmark == NULL) {
1305 			size = xsnprintf(hdr, sizeof hdr,
1306 			    "[%u/%u]", data->oy, screen_hsize(data->backing));
1307 		} else {
1308 			if (data->searchthis == -1) {
1309 				size = xsnprintf(hdr, sizeof hdr,
1310 				    "(%u results) [%d/%u]", data->searchcount,
1311 				    data->oy, screen_hsize(data->backing));
1312 			} else {
1313 				size = xsnprintf(hdr, sizeof hdr,
1314 				    "(%u/%u results) [%d/%u]", data->searchthis,
1315 				    data->searchcount, data->oy,
1316 				    screen_hsize(data->backing));
1317 			}
1318 		}
1319 		if (size > screen_size_x(s))
1320 			size = screen_size_x(s);
1321 		screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
1322 		screen_write_puts(ctx, &gc, "%s", hdr);
1323 	} else
1324 		size = 0;
1325 
1326 	if (size < screen_size_x(s)) {
1327 		screen_write_cursormove(ctx, 0, py);
1328 		screen_write_copy(ctx, data->backing, 0,
1329 		    (screen_hsize(data->backing) - data->oy) + py,
1330 		    screen_size_x(s) - size, 1, data->searchmark, &gc);
1331 	}
1332 
1333 	if (py == data->cy && data->cx == screen_size_x(s)) {
1334 		memcpy(&gc, &grid_default_cell, sizeof gc);
1335 		screen_write_cursormove(ctx, screen_size_x(s) - 1, py);
1336 		screen_write_putc(ctx, &gc, '$');
1337 	}
1338 }
1339 
1340 static void
1341 window_copy_write_lines(struct window_pane *wp, struct screen_write_ctx *ctx,
1342     u_int py, u_int ny)
1343 {
1344 	u_int	yy;
1345 
1346 	for (yy = py; yy < py + ny; yy++)
1347 		window_copy_write_line(wp, ctx, py);
1348 }
1349 
1350 static void
1351 window_copy_redraw_selection(struct window_pane *wp, u_int old_y)
1352 {
1353 	struct window_copy_mode_data	*data = wp->modedata;
1354 	u_int				 new_y, start, end;
1355 
1356 	new_y = data->cy;
1357 	if (old_y <= new_y) {
1358 		start = old_y;
1359 		end = new_y;
1360 	} else {
1361 		start = new_y;
1362 		end = old_y;
1363 	}
1364 	window_copy_redraw_lines(wp, start, end - start + 1);
1365 }
1366 
1367 static void
1368 window_copy_redraw_lines(struct window_pane *wp, u_int py, u_int ny)
1369 {
1370 	struct window_copy_mode_data	*data = wp->modedata;
1371 	struct screen_write_ctx	 	 ctx;
1372 	u_int				 i;
1373 
1374 	screen_write_start(&ctx, wp, NULL);
1375 	for (i = py; i < py + ny; i++)
1376 		window_copy_write_line(wp, &ctx, i);
1377 	screen_write_cursormove(&ctx, data->cx, data->cy);
1378 	screen_write_stop(&ctx);
1379 }
1380 
1381 static void
1382 window_copy_redraw_screen(struct window_pane *wp)
1383 {
1384 	struct window_copy_mode_data	*data = wp->modedata;
1385 
1386 	window_copy_redraw_lines(wp, 0, screen_size_y(&data->screen));
1387 }
1388 
1389 static void
1390 window_copy_synchronize_cursor(struct window_pane *wp)
1391 {
1392 	struct window_copy_mode_data	*data = wp->modedata;
1393 	u_int				 xx, yy;
1394 
1395 	xx = data->cx;
1396 	yy = screen_hsize(data->backing) + data->cy - data->oy;
1397 
1398 	switch (data->cursordrag) {
1399 	case CURSORDRAG_ENDSEL:
1400 		data->endselx = xx;
1401 		data->endsely = yy;
1402 		break;
1403 	case CURSORDRAG_SEL:
1404 		data->selx = xx;
1405 		data->sely = yy;
1406 		break;
1407 	case CURSORDRAG_NONE:
1408 		break;
1409 	}
1410 }
1411 
1412 static void
1413 window_copy_update_cursor(struct window_pane *wp, u_int cx, u_int cy)
1414 {
1415 	struct window_copy_mode_data	*data = wp->modedata;
1416 	struct screen			*s = &data->screen;
1417 	struct screen_write_ctx		 ctx;
1418 	u_int				 old_cx, old_cy;
1419 
1420 	old_cx = data->cx; old_cy = data->cy;
1421 	data->cx = cx; data->cy = cy;
1422 	if (old_cx == screen_size_x(s))
1423 		window_copy_redraw_lines(wp, old_cy, 1);
1424 	if (data->cx == screen_size_x(s))
1425 		window_copy_redraw_lines(wp, data->cy, 1);
1426 	else {
1427 		screen_write_start(&ctx, wp, NULL);
1428 		screen_write_cursormove(&ctx, data->cx, data->cy);
1429 		screen_write_stop(&ctx);
1430 	}
1431 }
1432 
1433 static void
1434 window_copy_start_selection(struct window_pane *wp)
1435 {
1436 	struct window_copy_mode_data	*data = wp->modedata;
1437 	struct screen			*s = &data->screen;
1438 
1439 	data->selx = data->cx;
1440 	data->sely = screen_hsize(data->backing) + data->cy - data->oy;
1441 
1442 	data->endselx = data->selx;
1443 	data->endsely = data->sely;
1444 
1445 	data->cursordrag = CURSORDRAG_ENDSEL;
1446 
1447 	s->sel.flag = 1;
1448 	window_copy_update_selection(wp, 1);
1449 }
1450 
1451 static int
1452 window_copy_adjust_selection(struct window_pane *wp, u_int *selx, u_int *sely)
1453 {
1454 	struct window_copy_mode_data	*data = wp->modedata;
1455 	struct screen			*s = &data->screen;
1456 	u_int 				 sx, sy, ty;
1457 	int				 relpos;
1458 
1459 	sx = *selx;
1460 	sy = *sely;
1461 
1462 	ty = screen_hsize(data->backing) - data->oy;
1463 	if (sy < ty) {
1464 		relpos = WINDOW_COPY_REL_POS_ABOVE;
1465 		if (!data->rectflag)
1466 			sx = 0;
1467 		sy = 0;
1468 	} else if (sy > ty + screen_size_y(s) - 1) {
1469 		relpos = WINDOW_COPY_REL_POS_BELOW;
1470 		if (!data->rectflag)
1471 			sx = screen_size_x(s) - 1;
1472 		sy = screen_size_y(s) - 1;
1473 	} else {
1474 		relpos = WINDOW_COPY_REL_POS_ON_SCREEN;
1475 		sy -= ty;
1476 	}
1477 
1478 	*selx = sx;
1479 	*sely = sy;
1480 	return (relpos);
1481 }
1482 
1483 static int
1484 window_copy_update_selection(struct window_pane *wp, int may_redraw)
1485 {
1486 	struct window_copy_mode_data	*data = wp->modedata;
1487 	struct screen			*s = &data->screen;
1488 	struct options			*oo = wp->window->options;
1489 	struct grid_cell		 gc;
1490 	u_int				 sx, sy, cy, endsx, endsy;
1491 	int				 startrelpos, endrelpos;
1492 
1493 	if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
1494 		return (0);
1495 
1496 	window_copy_synchronize_cursor(wp);
1497 
1498 	/* Adjust the selection. */
1499 	sx = data->selx;
1500 	sy = data->sely;
1501 	startrelpos = window_copy_adjust_selection(wp, &sx, &sy);
1502 
1503 	/* Adjust the end of selection. */
1504 	endsx = data->endselx;
1505 	endsy = data->endsely;
1506 	endrelpos = window_copy_adjust_selection(wp, &endsx, &endsy);
1507 
1508 	/* Selection is outside of the current screen */
1509 	if (startrelpos == endrelpos &&
1510 	    startrelpos != WINDOW_COPY_REL_POS_ON_SCREEN) {
1511 		screen_hide_selection(s);
1512 		return (0);
1513 	}
1514 
1515 	/* Set colours and selection. */
1516 	style_apply(&gc, oo, "mode-style");
1517 	gc.flags |= GRID_FLAG_NOPALETTE;
1518 	screen_set_selection(s, sx, sy, endsx, endsy, data->rectflag, &gc);
1519 
1520 	if (data->rectflag && may_redraw) {
1521 		/*
1522 		 * Can't rely on the caller to redraw the right lines for
1523 		 * rectangle selection - find the highest line and the number
1524 		 * of lines, and redraw just past that in both directions
1525 		 */
1526 		cy = data->cy;
1527 		if (data->cursordrag == CURSORDRAG_ENDSEL) {
1528 			if (sy < cy)
1529 				window_copy_redraw_lines(wp, sy, cy - sy + 1);
1530 			else
1531 				window_copy_redraw_lines(wp, cy, sy - cy + 1);
1532 		} else {
1533 			if (endsy < cy)
1534 				window_copy_redraw_lines(wp, endsy, cy - endsy + 1);
1535 			else
1536 				window_copy_redraw_lines(wp, cy, endsy - cy + 1);
1537 		}
1538 	}
1539 
1540 	return (1);
1541 }
1542 
1543 static void *
1544 window_copy_get_selection(struct window_pane *wp, size_t *len)
1545 {
1546 	struct window_copy_mode_data	*data = wp->modedata;
1547 	struct screen			*s = &data->screen;
1548 	char				*buf;
1549 	size_t				 off;
1550 	u_int				 i, xx, yy, sx, sy, ex, ey, ey_last;
1551 	u_int				 firstsx, lastex, restex, restsx;
1552 	int				 keys;
1553 
1554 	if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
1555 		return (NULL);
1556 
1557 	buf = xmalloc(1);
1558 	off = 0;
1559 
1560 	*buf = '\0';
1561 
1562 	/*
1563 	 * The selection extends from selx,sely to (adjusted) cx,cy on
1564 	 * the base screen.
1565 	 */
1566 
1567 	/* Find start and end. */
1568 	xx = data->endselx;
1569 	yy = data->endsely;
1570 	if (yy < data->sely || (yy == data->sely && xx < data->selx)) {
1571 		sx = xx; sy = yy;
1572 		ex = data->selx; ey = data->sely;
1573 	} else {
1574 		sx = data->selx; sy = data->sely;
1575 		ex = xx; ey = yy;
1576 	}
1577 
1578 	/* Trim ex to end of line. */
1579 	ey_last = window_copy_find_length(wp, ey);
1580 	if (ex > ey_last)
1581 		ex = ey_last;
1582 
1583 	/*
1584 	 * Deal with rectangle-copy if necessary; four situations: start of
1585 	 * first line (firstsx), end of last line (lastex), start (restsx) and
1586 	 * end (restex) of all other lines.
1587 	 */
1588 	xx = screen_size_x(s);
1589 
1590 	/*
1591 	 * Behave according to mode-keys. If it is emacs, copy like emacs,
1592 	 * keeping the top-left-most character, and dropping the
1593 	 * bottom-right-most, regardless of copy direction. If it is vi, also
1594 	 * keep bottom-right-most character.
1595 	 */
1596 	keys = options_get_number(wp->window->options, "mode-keys");
1597 	if (data->rectflag) {
1598 		/*
1599 		 * Need to ignore the column with the cursor in it, which for
1600 		 * rectangular copy means knowing which side the cursor is on.
1601 		 */
1602 		if (data->selx < data->cx) {
1603 			/* Selection start is on the left. */
1604 			if (keys == MODEKEY_EMACS) {
1605 				lastex = data->cx;
1606 				restex = data->cx;
1607 			}
1608 			else {
1609 				lastex = data->cx + 1;
1610 				restex = data->cx + 1;
1611 			}
1612 			firstsx = data->selx;
1613 			restsx = data->selx;
1614 		} else {
1615 			/* Cursor is on the left. */
1616 			lastex = data->selx + 1;
1617 			restex = data->selx + 1;
1618 			firstsx = data->cx;
1619 			restsx = data->cx;
1620 		}
1621 	} else {
1622 		if (keys == MODEKEY_EMACS)
1623 			lastex = ex;
1624 		else
1625 			lastex = ex + 1;
1626 		restex = xx;
1627 		firstsx = sx;
1628 		restsx = 0;
1629 	}
1630 
1631 	/* Copy the lines. */
1632 	for (i = sy; i <= ey; i++) {
1633 		window_copy_copy_line(wp, &buf, &off, i,
1634 		    (i == sy ? firstsx : restsx),
1635 		    (i == ey ? lastex : restex));
1636 	}
1637 
1638 	/* Don't bother if no data. */
1639 	if (off == 0) {
1640 		free(buf);
1641 		return (NULL);
1642 	}
1643 	if (keys == MODEKEY_EMACS || lastex <= ey_last)
1644 		off -= 1; /* remove final \n (unless at end in vi mode) */
1645 	*len = off;
1646 	return (buf);
1647 }
1648 
1649 static void
1650 window_copy_copy_buffer(struct window_pane *wp, const char *bufname, void *buf,
1651     size_t len)
1652 {
1653 	struct screen_write_ctx	ctx;
1654 
1655 	if (options_get_number(global_options, "set-clipboard") != 0) {
1656 		screen_write_start(&ctx, wp, NULL);
1657 		screen_write_setselection(&ctx, buf, len);
1658 		screen_write_stop(&ctx);
1659 		notify_pane("pane-set-clipboard", wp);
1660 	}
1661 
1662 	if (paste_set(buf, len, bufname, NULL) != 0)
1663 		free(buf);
1664 }
1665 
1666 static void
1667 window_copy_copy_pipe(struct window_pane *wp, struct session *s,
1668     const char *bufname, const char *arg)
1669 {
1670 	void		*buf;
1671 	size_t		 len;
1672 	struct job	*job;
1673 	char		*expanded;
1674 
1675 	buf = window_copy_get_selection(wp, &len);
1676 	if (buf == NULL)
1677 		return;
1678 	expanded = format_single(NULL, arg, NULL, s, NULL, wp);
1679 
1680 	job = job_run(expanded, s, NULL, NULL, NULL, NULL, NULL);
1681 	bufferevent_write(job->event, buf, len);
1682 
1683 	free(expanded);
1684 	window_copy_copy_buffer(wp, bufname, buf, len);
1685 }
1686 
1687 static void
1688 window_copy_copy_selection(struct window_pane *wp, const char *bufname)
1689 {
1690 	void	*buf;
1691 	size_t	 len;
1692 
1693 	buf = window_copy_get_selection(wp, &len);
1694 	if (buf == NULL)
1695 		return;
1696 
1697 	window_copy_copy_buffer(wp, bufname, buf, len);
1698 }
1699 
1700 static void
1701 window_copy_append_selection(struct window_pane *wp, const char *bufname)
1702 {
1703 	char				*buf;
1704 	struct paste_buffer		*pb;
1705 	const char			*bufdata;
1706 	size_t				 len, bufsize;
1707 	struct screen_write_ctx		 ctx;
1708 
1709 	buf = window_copy_get_selection(wp, &len);
1710 	if (buf == NULL)
1711 		return;
1712 
1713 	if (options_get_number(global_options, "set-clipboard") != 0) {
1714 		screen_write_start(&ctx, wp, NULL);
1715 		screen_write_setselection(&ctx, buf, len);
1716 		screen_write_stop(&ctx);
1717 		notify_pane("pane-set-clipboard", wp);
1718 	}
1719 
1720 	if (bufname == NULL || *bufname == '\0')
1721 		pb = paste_get_top(&bufname);
1722 	else
1723 		pb = paste_get_name(bufname);
1724 	if (pb != NULL) {
1725 		bufdata = paste_buffer_data(pb, &bufsize);
1726 		buf = xrealloc(buf, len + bufsize);
1727 		memmove(buf + bufsize, buf, len);
1728 		memcpy(buf, bufdata, bufsize);
1729 		len += bufsize;
1730 	}
1731 	if (paste_set(buf, len, bufname, NULL) != 0)
1732 		free(buf);
1733 }
1734 
1735 static void
1736 window_copy_copy_line(struct window_pane *wp, char **buf, size_t *off, u_int sy,
1737     u_int sx, u_int ex)
1738 {
1739 	struct window_copy_mode_data	*data = wp->modedata;
1740 	struct grid			*gd = data->backing->grid;
1741 	struct grid_cell		 gc;
1742 	struct grid_line		*gl;
1743 	struct utf8_data		 ud;
1744 	u_int				 i, xx, wrapped = 0;
1745 	const char			*s;
1746 
1747 	if (sx > ex)
1748 		return;
1749 
1750 	/*
1751 	 * Work out if the line was wrapped at the screen edge and all of it is
1752 	 * on screen.
1753 	 */
1754 	gl = &gd->linedata[sy];
1755 	if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
1756 		wrapped = 1;
1757 
1758 	/* If the line was wrapped, don't strip spaces (use the full length). */
1759 	if (wrapped)
1760 		xx = gl->cellsize;
1761 	else
1762 		xx = window_copy_find_length(wp, sy);
1763 	if (ex > xx)
1764 		ex = xx;
1765 	if (sx > xx)
1766 		sx = xx;
1767 
1768 	if (sx < ex) {
1769 		for (i = sx; i < ex; i++) {
1770 			grid_get_cell(gd, i, sy, &gc);
1771 			if (gc.flags & GRID_FLAG_PADDING)
1772 				continue;
1773 			utf8_copy(&ud, &gc.data);
1774 			if (ud.size == 1 && (gc.attr & GRID_ATTR_CHARSET)) {
1775 				s = tty_acs_get(NULL, ud.data[0]);
1776 				if (s != NULL && strlen(s) <= sizeof ud.data) {
1777 					ud.size = strlen(s);
1778 					memcpy(ud.data, s, ud.size);
1779 				}
1780 			}
1781 
1782 			*buf = xrealloc(*buf, (*off) + ud.size);
1783 			memcpy(*buf + *off, ud.data, ud.size);
1784 			*off += ud.size;
1785 		}
1786 	}
1787 
1788 	/* Only add a newline if the line wasn't wrapped. */
1789 	if (!wrapped || ex != xx) {
1790 		*buf = xrealloc(*buf, (*off) + 1);
1791 		(*buf)[(*off)++] = '\n';
1792 	}
1793 }
1794 
1795 static void
1796 window_copy_clear_selection(struct window_pane *wp)
1797 {
1798 	struct window_copy_mode_data   *data = wp->modedata;
1799 	u_int				px, py;
1800 
1801 	screen_clear_selection(&data->screen);
1802 
1803 	data->cursordrag = CURSORDRAG_NONE;
1804 
1805 	py = screen_hsize(data->backing) + data->cy - data->oy;
1806 	px = window_copy_find_length(wp, py);
1807 	if (data->cx > px)
1808 		window_copy_update_cursor(wp, px, data->cy);
1809 }
1810 
1811 static int
1812 window_copy_in_set(struct window_pane *wp, u_int px, u_int py, const char *set)
1813 {
1814 	struct window_copy_mode_data	*data = wp->modedata;
1815 	struct grid_cell		 gc;
1816 	const struct utf8_data		*ud;
1817 
1818 	grid_get_cell(data->backing->grid, px, py, &gc);
1819 
1820 	ud = &gc.data;
1821 	if (ud->size != 1 || (gc.flags & GRID_FLAG_PADDING))
1822 		return (0);
1823 	if (*ud->data == 0x00 || *ud->data == 0x7f)
1824 		return (0);
1825 	return (strchr(set, *ud->data) != NULL);
1826 }
1827 
1828 static u_int
1829 window_copy_find_length(struct window_pane *wp, u_int py)
1830 {
1831 	struct window_copy_mode_data	*data = wp->modedata;
1832 	struct screen			*s = data->backing;
1833 	struct grid_cell		 gc;
1834 	u_int				 px;
1835 
1836 	/*
1837 	 * If the pane has been resized, its grid can contain old overlong
1838 	 * lines. grid_peek_cell does not allow accessing cells beyond the
1839 	 * width of the grid, and screen_write_copy treats them as spaces, so
1840 	 * ignore them here too.
1841 	 */
1842 	px = s->grid->linedata[py].cellsize;
1843 	if (px > screen_size_x(s))
1844 		px = screen_size_x(s);
1845 	while (px > 0) {
1846 		grid_get_cell(s->grid, px - 1, py, &gc);
1847 		if (gc.data.size != 1 || *gc.data.data != ' ')
1848 			break;
1849 		px--;
1850 	}
1851 	return (px);
1852 }
1853 
1854 static void
1855 window_copy_cursor_start_of_line(struct window_pane *wp)
1856 {
1857 	struct window_copy_mode_data	*data = wp->modedata;
1858 	struct screen			*back_s = data->backing;
1859 	struct screen			*s = &data->screen;
1860 	struct grid			*gd = back_s->grid;
1861 	u_int				 py;
1862 
1863 	if (data->cx == 0 && s->sel.lineflag == LINE_SEL_NONE) {
1864 		py = screen_hsize(back_s) + data->cy - data->oy;
1865 		while (py > 0 &&
1866 		    gd->linedata[py-1].flags & GRID_LINE_WRAPPED) {
1867 			window_copy_cursor_up(wp, 0);
1868 			py = screen_hsize(back_s) + data->cy - data->oy;
1869 		}
1870 	}
1871 	window_copy_update_cursor(wp, 0, data->cy);
1872 	if (window_copy_update_selection(wp, 1))
1873 		window_copy_redraw_lines(wp, data->cy, 1);
1874 }
1875 
1876 static void
1877 window_copy_cursor_back_to_indentation(struct window_pane *wp)
1878 {
1879 	struct window_copy_mode_data	*data = wp->modedata;
1880 	u_int				 px, py, xx;
1881 	struct grid_cell		 gc;
1882 
1883 	px = 0;
1884 	py = screen_hsize(data->backing) + data->cy - data->oy;
1885 	xx = window_copy_find_length(wp, py);
1886 
1887 	while (px < xx) {
1888 		grid_get_cell(data->backing->grid, px, py, &gc);
1889 		if (gc.data.size != 1 || *gc.data.data != ' ')
1890 			break;
1891 		px++;
1892 	}
1893 
1894 	window_copy_update_cursor(wp, px, data->cy);
1895 	if (window_copy_update_selection(wp, 1))
1896 		window_copy_redraw_lines(wp, data->cy, 1);
1897 }
1898 
1899 static void
1900 window_copy_cursor_end_of_line(struct window_pane *wp)
1901 {
1902 	struct window_copy_mode_data	*data = wp->modedata;
1903 	struct screen			*back_s = data->backing;
1904 	struct screen			*s = &data->screen;
1905 	struct grid			*gd = back_s->grid;
1906 	u_int				 px, py;
1907 
1908 	py = screen_hsize(back_s) + data->cy - data->oy;
1909 	px = window_copy_find_length(wp, py);
1910 
1911 	if (data->cx == px && s->sel.lineflag == LINE_SEL_NONE) {
1912 		if (data->screen.sel.flag && data->rectflag)
1913 			px = screen_size_x(back_s);
1914 		if (gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1915 			while (py < gd->sy + gd->hsize &&
1916 			    gd->linedata[py].flags & GRID_LINE_WRAPPED) {
1917 				window_copy_cursor_down(wp, 0);
1918 				py = screen_hsize(back_s)
1919 				     + data->cy - data->oy;
1920 			}
1921 			px = window_copy_find_length(wp, py);
1922 		}
1923 	}
1924 	window_copy_update_cursor(wp, px, data->cy);
1925 
1926 	if (window_copy_update_selection(wp, 1))
1927 		window_copy_redraw_lines(wp, data->cy, 1);
1928 }
1929 
1930 static void
1931 window_copy_other_end(struct window_pane *wp)
1932 {
1933 	struct window_copy_mode_data	*data = wp->modedata;
1934 	struct screen			*s = &data->screen;
1935 	u_int				 selx, sely, cy, yy, hsize;
1936 
1937 	if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
1938 		return;
1939 
1940 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
1941 		s->sel.lineflag = LINE_SEL_RIGHT_LEFT;
1942 	else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
1943 		s->sel.lineflag = LINE_SEL_LEFT_RIGHT;
1944 
1945 	switch (data->cursordrag) {
1946 		case CURSORDRAG_NONE:
1947 		case CURSORDRAG_SEL:
1948 			data->cursordrag = CURSORDRAG_ENDSEL;
1949 			break;
1950 		case CURSORDRAG_ENDSEL:
1951 			data->cursordrag = CURSORDRAG_SEL;
1952 			break;
1953 	}
1954 
1955 	selx = data->endselx;
1956 	sely = data->endsely;
1957 	if (data->cursordrag == CURSORDRAG_SEL) {
1958 		selx = data->selx;
1959 		sely = data->sely;
1960 	}
1961 
1962 	cy = data->cy;
1963 	yy = screen_hsize(data->backing) + data->cy - data->oy;
1964 
1965 	data->cx = selx;
1966 
1967 	hsize = screen_hsize(data->backing);
1968 	if (sely < hsize - data->oy) { /* above */
1969 		data->oy = hsize - sely;
1970 		data->cy = 0;
1971 	} else if (sely > hsize - data->oy + screen_size_y(s)) { /* below */
1972 		data->oy = hsize - sely + screen_size_y(s) - 1;
1973 		data->cy = screen_size_y(s) - 1;
1974 	} else
1975 		data->cy = cy + sely - yy;
1976 
1977 	window_copy_update_selection(wp, 1);
1978 	window_copy_redraw_screen(wp);
1979 }
1980 
1981 static void
1982 window_copy_cursor_left(struct window_pane *wp)
1983 {
1984 	struct window_copy_mode_data	*data = wp->modedata;
1985 	u_int				 py, cx;
1986 	struct grid_cell		 gc;
1987 
1988 	py = screen_hsize(data->backing) + data->cy - data->oy;
1989 	cx = data->cx;
1990 	while (cx > 0) {
1991 		grid_get_cell(data->backing->grid, cx, py, &gc);
1992 		if (~gc.flags & GRID_FLAG_PADDING)
1993 			break;
1994 		cx--;
1995 	}
1996 	if (cx == 0 && py > 0) {
1997 		window_copy_cursor_up(wp, 0);
1998 		window_copy_cursor_end_of_line(wp);
1999 	} else if (cx > 0) {
2000 		window_copy_update_cursor(wp, cx - 1, data->cy);
2001 		if (window_copy_update_selection(wp, 1))
2002 			window_copy_redraw_lines(wp, data->cy, 1);
2003 	}
2004 }
2005 
2006 static void
2007 window_copy_cursor_right(struct window_pane *wp)
2008 {
2009 	struct window_copy_mode_data	*data = wp->modedata;
2010 	u_int				 px, py, yy, cx, cy;
2011 	struct grid_cell		 gc;
2012 
2013 	py = screen_hsize(data->backing) + data->cy - data->oy;
2014 	yy = screen_hsize(data->backing) + screen_size_y(data->backing) - 1;
2015 	if (data->screen.sel.flag && data->rectflag)
2016 		px = screen_size_x(&data->screen);
2017 	else
2018 		px = window_copy_find_length(wp, py);
2019 
2020 	if (data->cx >= px && py < yy) {
2021 		window_copy_cursor_start_of_line(wp);
2022 		window_copy_cursor_down(wp, 0);
2023 	} else if (data->cx < px) {
2024 		cx = data->cx + 1;
2025 		cy = screen_hsize(data->backing) + data->cy - data->oy;
2026 		while (cx < px) {
2027 			grid_get_cell(data->backing->grid, cx, cy, &gc);
2028 			if (~gc.flags & GRID_FLAG_PADDING)
2029 				break;
2030 			cx++;
2031 		}
2032 		window_copy_update_cursor(wp, cx, data->cy);
2033 		if (window_copy_update_selection(wp, 1))
2034 			window_copy_redraw_lines(wp, data->cy, 1);
2035 	}
2036 }
2037 
2038 static void
2039 window_copy_cursor_up(struct window_pane *wp, int scroll_only)
2040 {
2041 	struct window_copy_mode_data	*data = wp->modedata;
2042 	struct screen			*s = &data->screen;
2043 	u_int				 ox, oy, px, py;
2044 
2045 	oy = screen_hsize(data->backing) + data->cy - data->oy;
2046 	ox = window_copy_find_length(wp, oy);
2047 	if (data->cx != ox) {
2048 		data->lastcx = data->cx;
2049 		data->lastsx = ox;
2050 	}
2051 
2052 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
2053 		window_copy_other_end(wp);
2054 
2055 	data->cx = data->lastcx;
2056 	if (scroll_only || data->cy == 0) {
2057 		window_copy_scroll_down(wp, 1);
2058 		if (scroll_only) {
2059 			if (data->cy == screen_size_y(s) - 1)
2060 				window_copy_redraw_lines(wp, data->cy, 1);
2061 			else
2062 				window_copy_redraw_lines(wp, data->cy, 2);
2063 		}
2064 	} else {
2065 		window_copy_update_cursor(wp, data->cx, data->cy - 1);
2066 		if (window_copy_update_selection(wp, 1)) {
2067 			if (data->cy == screen_size_y(s) - 1)
2068 				window_copy_redraw_lines(wp, data->cy, 1);
2069 			else
2070 				window_copy_redraw_lines(wp, data->cy, 2);
2071 		}
2072 	}
2073 
2074 	if (!data->screen.sel.flag || !data->rectflag) {
2075 		py = screen_hsize(data->backing) + data->cy - data->oy;
2076 		px = window_copy_find_length(wp, py);
2077 		if ((data->cx >= data->lastsx && data->cx != px) ||
2078 		    data->cx > px)
2079 			window_copy_cursor_end_of_line(wp);
2080 	}
2081 
2082 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
2083 		window_copy_cursor_end_of_line(wp);
2084 	else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
2085 		window_copy_cursor_start_of_line(wp);
2086 }
2087 
2088 static void
2089 window_copy_cursor_down(struct window_pane *wp, int scroll_only)
2090 {
2091 	struct window_copy_mode_data	*data = wp->modedata;
2092 	struct screen			*s = &data->screen;
2093 	u_int				 ox, oy, px, py;
2094 
2095 	oy = screen_hsize(data->backing) + data->cy - data->oy;
2096 	ox = window_copy_find_length(wp, oy);
2097 	if (data->cx != ox) {
2098 		data->lastcx = data->cx;
2099 		data->lastsx = ox;
2100 	}
2101 
2102 	if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
2103 		window_copy_other_end(wp);
2104 
2105 	data->cx = data->lastcx;
2106 	if (scroll_only || data->cy == screen_size_y(s) - 1) {
2107 		window_copy_scroll_up(wp, 1);
2108 		if (scroll_only && data->cy > 0)
2109 			window_copy_redraw_lines(wp, data->cy - 1, 2);
2110 	} else {
2111 		window_copy_update_cursor(wp, data->cx, data->cy + 1);
2112 		if (window_copy_update_selection(wp, 1))
2113 			window_copy_redraw_lines(wp, data->cy - 1, 2);
2114 	}
2115 
2116 	if (!data->screen.sel.flag || !data->rectflag) {
2117 		py = screen_hsize(data->backing) + data->cy - data->oy;
2118 		px = window_copy_find_length(wp, py);
2119 		if ((data->cx >= data->lastsx && data->cx != px) ||
2120 		    data->cx > px)
2121 			window_copy_cursor_end_of_line(wp);
2122 	}
2123 
2124 	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
2125 		window_copy_cursor_end_of_line(wp);
2126 	else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
2127 		window_copy_cursor_start_of_line(wp);
2128 }
2129 
2130 static void
2131 window_copy_cursor_jump(struct window_pane *wp)
2132 {
2133 	struct window_copy_mode_data	*data = wp->modedata;
2134 	struct screen			*back_s = data->backing;
2135 	struct grid_cell		 gc;
2136 	u_int				 px, py, xx;
2137 
2138 	px = data->cx + 1;
2139 	py = screen_hsize(back_s) + data->cy - data->oy;
2140 	xx = window_copy_find_length(wp, py);
2141 
2142 	while (px < xx) {
2143 		grid_get_cell(back_s->grid, px, py, &gc);
2144 		if (!(gc.flags & GRID_FLAG_PADDING) &&
2145 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
2146 			window_copy_update_cursor(wp, px, data->cy);
2147 			if (window_copy_update_selection(wp, 1))
2148 				window_copy_redraw_lines(wp, data->cy, 1);
2149 			return;
2150 		}
2151 		px++;
2152 	}
2153 }
2154 
2155 static void
2156 window_copy_cursor_jump_back(struct window_pane *wp)
2157 {
2158 	struct window_copy_mode_data	*data = wp->modedata;
2159 	struct screen			*back_s = data->backing;
2160 	struct grid_cell		 gc;
2161 	u_int				 px, py;
2162 
2163 	px = data->cx;
2164 	py = screen_hsize(back_s) + data->cy - data->oy;
2165 
2166 	if (px > 0)
2167 		px--;
2168 
2169 	for (;;) {
2170 		grid_get_cell(back_s->grid, px, py, &gc);
2171 		if (!(gc.flags & GRID_FLAG_PADDING) &&
2172 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
2173 			window_copy_update_cursor(wp, px, data->cy);
2174 			if (window_copy_update_selection(wp, 1))
2175 				window_copy_redraw_lines(wp, data->cy, 1);
2176 			return;
2177 		}
2178 		if (px == 0)
2179 			break;
2180 		px--;
2181 	}
2182 }
2183 
2184 static void
2185 window_copy_cursor_jump_to(struct window_pane *wp)
2186 {
2187 	struct window_copy_mode_data	*data = wp->modedata;
2188 	struct screen			*back_s = data->backing;
2189 	struct grid_cell		 gc;
2190 	u_int				 px, py, xx;
2191 
2192 	px = data->cx + 2;
2193 	py = screen_hsize(back_s) + data->cy - data->oy;
2194 	xx = window_copy_find_length(wp, py);
2195 
2196 	while (px < xx) {
2197 		grid_get_cell(back_s->grid, px, py, &gc);
2198 		if (!(gc.flags & GRID_FLAG_PADDING) &&
2199 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
2200 			window_copy_update_cursor(wp, px - 1, data->cy);
2201 			if (window_copy_update_selection(wp, 1))
2202 				window_copy_redraw_lines(wp, data->cy, 1);
2203 			return;
2204 		}
2205 		px++;
2206 	}
2207 }
2208 
2209 static void
2210 window_copy_cursor_jump_to_back(struct window_pane *wp)
2211 {
2212 	struct window_copy_mode_data	*data = wp->modedata;
2213 	struct screen			*back_s = data->backing;
2214 	struct grid_cell		 gc;
2215 	u_int				 px, py;
2216 
2217 	px = data->cx;
2218 	py = screen_hsize(back_s) + data->cy - data->oy;
2219 
2220 	if (px > 0)
2221 		px--;
2222 
2223 	if (px > 0)
2224 		px--;
2225 
2226 	for (;;) {
2227 		grid_get_cell(back_s->grid, px, py, &gc);
2228 		if (!(gc.flags & GRID_FLAG_PADDING) &&
2229 		    gc.data.size == 1 && *gc.data.data == data->jumpchar) {
2230 			window_copy_update_cursor(wp, px + 1, data->cy);
2231 			if (window_copy_update_selection(wp, 1))
2232 				window_copy_redraw_lines(wp, data->cy, 1);
2233 			return;
2234 		}
2235 		if (px == 0)
2236 			break;
2237 		px--;
2238 	}
2239 }
2240 
2241 static void
2242 window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
2243 {
2244 	struct window_copy_mode_data	*data = wp->modedata;
2245 	struct screen			*back_s = data->backing;
2246 	u_int				 px, py, xx, yy;
2247 	int				 expected = 0;
2248 
2249 	px = data->cx;
2250 	py = screen_hsize(back_s) + data->cy - data->oy;
2251 	xx = window_copy_find_length(wp, py);
2252 	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
2253 
2254 	/*
2255 	 * First skip past any nonword characters and then any word characters.
2256 	 *
2257 	 * expected is initially set to 0 for the former and then 1 for the
2258 	 * latter.
2259 	 */
2260 	do {
2261 		while (px > xx ||
2262 		    window_copy_in_set(wp, px, py, separators) == expected) {
2263 			/* Move down if we're past the end of the line. */
2264 			if (px > xx) {
2265 				if (py == yy)
2266 					return;
2267 				window_copy_cursor_down(wp, 0);
2268 				px = 0;
2269 
2270 				py = screen_hsize(back_s) + data->cy - data->oy;
2271 				xx = window_copy_find_length(wp, py);
2272 			} else
2273 				px++;
2274 		}
2275 		expected = !expected;
2276 	} while (expected == 1);
2277 
2278 	window_copy_update_cursor(wp, px, data->cy);
2279 	if (window_copy_update_selection(wp, 1))
2280 		window_copy_redraw_lines(wp, data->cy, 1);
2281 }
2282 
2283 static void
2284 window_copy_cursor_next_word_end(struct window_pane *wp,
2285     const char *separators)
2286 {
2287 	struct window_copy_mode_data	*data = wp->modedata;
2288 	struct options			*oo = wp->window->options;
2289 	struct screen			*back_s = data->backing;
2290 	u_int				 px, py, xx, yy;
2291 	int				 keys, expected = 1;
2292 
2293 	px = data->cx;
2294 	py = screen_hsize(back_s) + data->cy - data->oy;
2295 	xx = window_copy_find_length(wp, py);
2296 	yy = screen_hsize(back_s) + screen_size_y(back_s) - 1;
2297 
2298 	keys = options_get_number(oo, "mode-keys");
2299 	if (keys == MODEKEY_VI && !window_copy_in_set(wp, px, py, separators))
2300 		px++;
2301 
2302 	/*
2303 	 * First skip past any word characters, then any nonword characters.
2304 	 *
2305 	 * expected is initially set to 1 for the former and then 0 for the
2306 	 * latter.
2307 	 */
2308 	do {
2309 		while (px > xx ||
2310 		    window_copy_in_set(wp, px, py, separators) == expected) {
2311 			/* Move down if we're past the end of the line. */
2312 			if (px > xx) {
2313 				if (py == yy)
2314 					return;
2315 				window_copy_cursor_down(wp, 0);
2316 				px = 0;
2317 
2318 				py = screen_hsize(back_s) + data->cy - data->oy;
2319 				xx = window_copy_find_length(wp, py);
2320 			} else
2321 				px++;
2322 		}
2323 		expected = !expected;
2324 	} while (expected == 0);
2325 
2326 	if (keys == MODEKEY_VI && px != 0)
2327 		px--;
2328 
2329 	window_copy_update_cursor(wp, px, data->cy);
2330 	if (window_copy_update_selection(wp, 1))
2331 		window_copy_redraw_lines(wp, data->cy, 1);
2332 }
2333 
2334 /* Move to the previous place where a word begins. */
2335 static void
2336 window_copy_cursor_previous_word(struct window_pane *wp,
2337     const char *separators)
2338 {
2339 	struct window_copy_mode_data	*data = wp->modedata;
2340 	u_int				 px, py;
2341 
2342 	px = data->cx;
2343 	py = screen_hsize(data->backing) + data->cy - data->oy;
2344 
2345 	/* Move back to the previous word character. */
2346 	for (;;) {
2347 		if (px > 0) {
2348 			px--;
2349 			if (!window_copy_in_set(wp, px, py, separators))
2350 				break;
2351 		} else {
2352 			if (data->cy == 0 &&
2353 			    (screen_hsize(data->backing) == 0 ||
2354 			    data->oy >= screen_hsize(data->backing) - 1))
2355 				goto out;
2356 			window_copy_cursor_up(wp, 0);
2357 
2358 			py = screen_hsize(data->backing) + data->cy - data->oy;
2359 			px = window_copy_find_length(wp, py);
2360 		}
2361 	}
2362 
2363 	/* Move back to the beginning of this word. */
2364 	while (px > 0 && !window_copy_in_set(wp, px - 1, py, separators))
2365 		px--;
2366 
2367 out:
2368 	window_copy_update_cursor(wp, px, data->cy);
2369 	if (window_copy_update_selection(wp, 1))
2370 		window_copy_redraw_lines(wp, data->cy, 1);
2371 }
2372 
2373 static void
2374 window_copy_scroll_up(struct window_pane *wp, u_int ny)
2375 {
2376 	struct window_copy_mode_data	*data = wp->modedata;
2377 	struct screen			*s = &data->screen;
2378 	struct screen_write_ctx		 ctx;
2379 
2380 	if (data->oy < ny)
2381 		ny = data->oy;
2382 	if (ny == 0)
2383 		return;
2384 	data->oy -= ny;
2385 
2386 	window_copy_update_selection(wp, 0);
2387 
2388 	screen_write_start(&ctx, wp, NULL);
2389 	screen_write_cursormove(&ctx, 0, 0);
2390 	screen_write_deleteline(&ctx, ny, 8);
2391 	window_copy_write_lines(wp, &ctx, screen_size_y(s) - ny, ny);
2392 	window_copy_write_line(wp, &ctx, 0);
2393 	if (screen_size_y(s) > 1)
2394 		window_copy_write_line(wp, &ctx, 1);
2395 	if (screen_size_y(s) > 3)
2396 		window_copy_write_line(wp, &ctx, screen_size_y(s) - 2);
2397 	if (s->sel.flag && screen_size_y(s) > ny)
2398 		window_copy_write_line(wp, &ctx, screen_size_y(s) - ny - 1);
2399 	screen_write_cursormove(&ctx, data->cx, data->cy);
2400 	screen_write_stop(&ctx);
2401 }
2402 
2403 static void
2404 window_copy_scroll_down(struct window_pane *wp, u_int ny)
2405 {
2406 	struct window_copy_mode_data	*data = wp->modedata;
2407 	struct screen			*s = &data->screen;
2408 	struct screen_write_ctx		 ctx;
2409 
2410 	if (ny > screen_hsize(data->backing))
2411 		return;
2412 
2413 	if (data->oy > screen_hsize(data->backing) - ny)
2414 		ny = screen_hsize(data->backing) - data->oy;
2415 	if (ny == 0)
2416 		return;
2417 	data->oy += ny;
2418 
2419 	window_copy_update_selection(wp, 0);
2420 
2421 	screen_write_start(&ctx, wp, NULL);
2422 	screen_write_cursormove(&ctx, 0, 0);
2423 	screen_write_insertline(&ctx, ny, 8);
2424 	window_copy_write_lines(wp, &ctx, 0, ny);
2425 	if (s->sel.flag && screen_size_y(s) > ny)
2426 		window_copy_write_line(wp, &ctx, ny);
2427 	else if (ny == 1) /* nuke position */
2428 		window_copy_write_line(wp, &ctx, 1);
2429 	screen_write_cursormove(&ctx, data->cx, data->cy);
2430 	screen_write_stop(&ctx);
2431 }
2432 
2433 void
2434 window_copy_add_formats(struct window_pane *wp, struct format_tree *ft)
2435 {
2436 	struct window_copy_mode_data	*data = wp->modedata;
2437 	struct screen			*s = &data->screen;
2438 
2439 	if (wp->mode != &window_copy_mode)
2440 		return;
2441 
2442 	format_add(ft, "selection_present", "%d", s->sel.flag);
2443 	format_add(ft, "scroll_position", "%d", data->oy);
2444 }
2445 
2446 static void
2447 window_copy_rectangle_toggle(struct window_pane *wp)
2448 {
2449 	struct window_copy_mode_data	*data = wp->modedata;
2450 	u_int				 px, py;
2451 
2452 	data->rectflag = !data->rectflag;
2453 
2454 	py = screen_hsize(data->backing) + data->cy - data->oy;
2455 	px = window_copy_find_length(wp, py);
2456 	if (data->cx > px)
2457 		window_copy_update_cursor(wp, px, data->cy);
2458 
2459 	window_copy_update_selection(wp, 1);
2460 	window_copy_redraw_screen(wp);
2461 }
2462 
2463 static void
2464 window_copy_move_mouse(struct mouse_event *m)
2465 {
2466 	struct window_pane	*wp;
2467 	u_int			 x, y;
2468 
2469 	wp = cmd_mouse_pane(m, NULL, NULL);
2470 	if (wp == NULL || wp->mode != &window_copy_mode)
2471 		return;
2472 
2473 	if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
2474 		return;
2475 
2476 	window_copy_update_cursor(wp, x, y);
2477 }
2478 
2479 void
2480 window_copy_start_drag(struct client *c, struct mouse_event *m)
2481 {
2482 	struct window_pane	*wp;
2483 	u_int			 x, y;
2484 
2485 	if (c == NULL)
2486 		return;
2487 
2488 	wp = cmd_mouse_pane(m, NULL, NULL);
2489 	if (wp == NULL || wp->mode != &window_copy_mode)
2490 		return;
2491 
2492 	if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
2493 		return;
2494 
2495 	c->tty.mouse_drag_update = window_copy_drag_update;
2496 	c->tty.mouse_drag_release = NULL; /* will fire MouseDragEnd key */
2497 
2498 	window_copy_update_cursor(wp, x, y);
2499 	window_copy_start_selection(wp);
2500 	window_copy_redraw_screen(wp);
2501 }
2502 
2503 static void
2504 window_copy_drag_update(__unused struct client *c, struct mouse_event *m)
2505 {
2506 	struct window_pane		*wp;
2507 	struct window_copy_mode_data	*data;
2508 	u_int				 x, y, old_cy;
2509 
2510 	wp = cmd_mouse_pane(m, NULL, NULL);
2511 	if (wp == NULL || wp->mode != &window_copy_mode)
2512 		return;
2513 	data = wp->modedata;
2514 
2515 	if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
2516 		return;
2517 	old_cy = data->cy;
2518 
2519 	window_copy_update_cursor(wp, x, y);
2520 	if (window_copy_update_selection(wp, 1))
2521 		window_copy_redraw_selection(wp, old_cy);
2522 }
2523