GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/window-copy.c Lines: 0 1283 0.0 %
Date: 2017-11-07 Branches: 0 897 0.0 %

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