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