1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2007 Nicholas Marriott <[email protected]>
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
26#include "tmux.h"
27
28static const char *window_copy_key_table(struct window_mode_entry *);
29static void window_copy_command(struct window_mode_entry *, struct client *,
30 struct session *, struct winlink *, struct args *,
31 struct mouse_event *);
32static struct screen *window_copy_init(struct window_mode_entry *,
33 struct cmd_find_state *, struct args *);
34static struct screen *window_copy_view_init(struct window_mode_entry *,
35 struct cmd_find_state *, struct args *);
36static void window_copy_free(struct window_mode_entry *);
37static void window_copy_resize(struct window_mode_entry *, u_int, u_int);
38static void window_copy_formats(struct window_mode_entry *,
39 struct format_tree *);
40static void window_copy_pageup1(struct window_mode_entry *, int);
41static int window_copy_pagedown(struct window_mode_entry *, int, int);
42static void window_copy_next_paragraph(struct window_mode_entry *);
43static void window_copy_previous_paragraph(struct window_mode_entry *);
44
45static void window_copy_redraw_selection(struct window_mode_entry *, u_int);
46static void window_copy_redraw_lines(struct window_mode_entry *, u_int,
47 u_int);
48static void window_copy_redraw_screen(struct window_mode_entry *);
49static void window_copy_write_line(struct window_mode_entry *,
50 struct screen_write_ctx *, u_int);
51static void window_copy_write_lines(struct window_mode_entry *,
52 struct screen_write_ctx *, u_int, u_int);
53
54static void window_copy_scroll_to(struct window_mode_entry *, u_int, u_int);
55static int window_copy_search_compare(struct grid *, u_int, u_int,
56 struct grid *, u_int, int);
57static int window_copy_search_lr(struct grid *, struct grid *, u_int *,
58 u_int, u_int, u_int, int);
59static int window_copy_search_rl(struct grid *, struct grid *, u_int *,
60 u_int, u_int, u_int, int);
61static int window_copy_last_regex(struct grid *gd, u_int py, u_int first,
62 u_int last, u_int len, u_int *ppx, u_int *psx,
63 const char *buf, const regex_t *preg, int eflags);
64static char *window_copy_stringify(struct grid *, u_int, u_int, u_int,
65 char *, u_int *);
66static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, u_int *,
67 const char *str);
68static int window_copy_search_marks(struct window_mode_entry *,
69 struct screen *, int);
70static void window_copy_clear_marks(struct window_mode_entry *);
71static void window_copy_move_left(struct screen *, u_int *, u_int *, int);
72static void window_copy_move_right(struct screen *, u_int *, u_int *, int);
73static int window_copy_is_lowercase(const char *);
74static int window_copy_search_jump(struct window_mode_entry *,
75 struct grid *, struct grid *, u_int, u_int, u_int, int, int,
76 int, int);
77static int window_copy_search(struct window_mode_entry *, int, int);
78static int window_copy_search_up(struct window_mode_entry *, int);
79static int window_copy_search_down(struct window_mode_entry *, int);
80static void window_copy_goto_line(struct window_mode_entry *, const char *);
81static void window_copy_update_cursor(struct window_mode_entry *, u_int,
82 u_int);
83static void window_copy_start_selection(struct window_mode_entry *);
84static int window_copy_adjust_selection(struct window_mode_entry *,
85 u_int *, u_int *);
86static int window_copy_set_selection(struct window_mode_entry *, int, int);
87static int window_copy_update_selection(struct window_mode_entry *, int,
88 int);
89static void window_copy_synchronize_cursor(struct window_mode_entry *, int);
90static void *window_copy_get_selection(struct window_mode_entry *, size_t *);
91static void window_copy_copy_buffer(struct window_mode_entry *,
92 const char *, void *, size_t);
93static void window_copy_copy_pipe(struct window_mode_entry *,
94 struct session *, const char *, const char *);
95static void window_copy_copy_selection(struct window_mode_entry *,
96 const char *);
97static void window_copy_append_selection(struct window_mode_entry *);
98static void window_copy_clear_selection(struct window_mode_entry *);
99static void window_copy_copy_line(struct window_mode_entry *, char **,
100 size_t *, u_int, u_int, u_int);
101static int window_copy_in_set(struct window_mode_entry *, u_int, u_int,
102 const char *);
103static u_int window_copy_find_length(struct window_mode_entry *, u_int);
104static void window_copy_cursor_start_of_line(struct window_mode_entry *);
105static void window_copy_cursor_back_to_indentation(
106 struct window_mode_entry *);
107static void window_copy_cursor_end_of_line(struct window_mode_entry *);
108static void window_copy_other_end(struct window_mode_entry *);
109static void window_copy_cursor_left(struct window_mode_entry *);
110static void window_copy_cursor_right(struct window_mode_entry *);
111static void window_copy_cursor_up(struct window_mode_entry *, int);
112static void window_copy_cursor_down(struct window_mode_entry *, int);
113static void window_copy_cursor_jump(struct window_mode_entry *);
114static void window_copy_cursor_jump_back(struct window_mode_entry *);
115static void window_copy_cursor_jump_to(struct window_mode_entry *);
116static void window_copy_cursor_jump_to_back(struct window_mode_entry *);
117static void window_copy_cursor_next_word(struct window_mode_entry *,
118 const char *);
119static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *,
120 const char *, u_int *, u_int *);
121static void window_copy_cursor_next_word_end(struct window_mode_entry *,
122 const char *, int);
123static void window_copy_cursor_previous_word_pos(struct window_mode_entry *,
124 const char *, int, u_int *, u_int *);
125static void window_copy_cursor_previous_word(struct window_mode_entry *,
126 const char *, int);
127static void window_copy_scroll_up(struct window_mode_entry *, u_int);
128static void window_copy_scroll_down(struct window_mode_entry *, u_int);
129static void window_copy_rectangle_toggle(struct window_mode_entry *);
130static void window_copy_move_mouse(struct mouse_event *);
131static void window_copy_drag_update(struct client *, struct mouse_event *);
132static void window_copy_drag_release(struct client *, struct mouse_event *);
133
134const struct window_mode window_copy_mode = {
135 .name = "copy-mode",
136
137 .init = window_copy_init,
138 .free = window_copy_free,
139 .resize = window_copy_resize,
140 .key_table = window_copy_key_table,
141 .command = window_copy_command,
142 .formats = window_copy_formats,
143};
144
145const struct window_mode window_view_mode = {
146 .name = "view-mode",
147
148 .init = window_copy_view_init,
149 .free = window_copy_free,
150 .resize = window_copy_resize,
151 .key_table = window_copy_key_table,
152 .command = window_copy_command,
153 .formats = window_copy_formats,
154};
155
156enum {
157 WINDOW_COPY_OFF,
158 WINDOW_COPY_SEARCHUP,
159 WINDOW_COPY_SEARCHDOWN,
160 WINDOW_COPY_JUMPFORWARD,
161 WINDOW_COPY_JUMPBACKWARD,
162 WINDOW_COPY_JUMPTOFORWARD,
163 WINDOW_COPY_JUMPTOBACKWARD,
164};
165
166enum {
167 WINDOW_COPY_REL_POS_ABOVE,
168 WINDOW_COPY_REL_POS_ON_SCREEN,
169 WINDOW_COPY_REL_POS_BELOW,
170};
171
172enum window_copy_cmd_action {
173 WINDOW_COPY_CMD_NOTHING,
174 WINDOW_COPY_CMD_REDRAW,
175 WINDOW_COPY_CMD_CANCEL,
176};
177
178struct window_copy_cmd_state {
179 struct window_mode_entry *wme;
180 struct args *args;
181 struct mouse_event *m;
182
183 struct client *c;
184 struct session *s;
185 struct winlink *wl;
186};
187
188/*
189 * Copy mode's visible screen (the "screen" field) is filled from one of two
190 * sources: the original contents of the pane (used when we actually enter via
191 * the "copy-mode" command, to copy the contents of the current pane), or else
192 * a series of lines containing the output from an output-writing tmux command
193 * (such as any of the "show-*" or "list-*" commands).
194 *
195 * In either case, the full content of the copy-mode grid is pointed at by the
196 * "backing" field, and is copied into "screen" as needed (that is, when
197 * scrolling occurs). When copy-mode is backed by a pane, backing points
198 * directly at that pane's screen structure (&wp->base); when backed by a list
199 * of output-lines from a command, it points at a newly-allocated screen
200 * structure (which is deallocated when the mode ends).
201 */
202struct window_copy_mode_data {
203 struct screen screen;
204
205 struct screen *backing;
206 int backing_written; /* backing display started */
207
208 u_int oy; /* number of lines scrolled up */
209
210 u_int selx; /* beginning of selection */
211 u_int sely;
212
213 u_int endselx; /* end of selection */
214 u_int endsely;
215
216 enum {
217 CURSORDRAG_NONE, /* selection is independent of cursor */
218 CURSORDRAG_ENDSEL, /* end is synchronized with cursor */
219 CURSORDRAG_SEL, /* start is synchronized with cursor */
220 } cursordrag;
221
222 int modekeys;
223 enum {
224 LINE_SEL_NONE,
225 LINE_SEL_LEFT_RIGHT,
226 LINE_SEL_RIGHT_LEFT,
227 } lineflag; /* line selection mode */
228 int rectflag; /* in rectangle copy mode? */
229 int scroll_exit; /* exit on scroll to end? */
230 int hide_position; /* hide position marker */
231
232 enum {
233 SEL_CHAR, /* select one char at a time */
234 SEL_WORD, /* select one word at a time */
235 SEL_LINE, /* select one line at a time */
236 } selflag;
237
238 const char *ws; /* word separators */
239
240 u_int dx; /* drag start position */
241 u_int dy;
242
243 u_int selrx; /* selection reset positions */
244 u_int selry;
245 u_int endselrx;
246 u_int endselry;
247
248 u_int cx;
249 u_int cy;
250
251 u_int lastcx; /* position in last line w/ content */
252 u_int lastsx; /* size of last line w/ content */
253
254 int searchtype;
255 int searchregex;
256 char *searchstr;
257 bitstr_t *searchmark;
258 u_int searchcount;
259 int searchthis;
260 int searchx;
261 int searchy;
262 int searcho;
263
264 int timeout; /* search has timed out */
265#define WINDOW_COPY_SEARCH_TIMEOUT 10
266
267 int jumptype;
268 char jumpchar;
269
270 struct event dragtimer;
271#define WINDOW_COPY_DRAG_REPEAT_TIME 50000
272};
273
274static void
275window_copy_scroll_timer(__unused int fd, __unused short events, void *arg)
276{
277 struct window_mode_entry *wme = arg;
278 struct window_pane *wp = wme->wp;
279 struct window_copy_mode_data *data = wme->data;
280 struct timeval tv = {
281 .tv_usec = WINDOW_COPY_DRAG_REPEAT_TIME
282 };
283
284 evtimer_del(&data->dragtimer);
285
286 if (TAILQ_FIRST(&wp->modes) != wme)
287 return;
288
289 if (data->cy == 0) {
290 evtimer_add(&data->dragtimer, &tv);
291 window_copy_cursor_up(wme, 1);
292 } else if (data->cy == screen_size_y(&data->screen) - 1) {
293 evtimer_add(&data->dragtimer, &tv);
294 window_copy_cursor_down(wme, 1);
295 }
296}
297
298static struct window_copy_mode_data *
299window_copy_common_init(struct window_mode_entry *wme)
300{
301 struct window_pane *wp = wme->wp;
302 struct window_copy_mode_data *data;
303 struct screen *base = &wp->base;
304
305 wme->data = data = xcalloc(1, sizeof *data);
306
307 data->cursordrag = CURSORDRAG_NONE;
308 data->lineflag = LINE_SEL_NONE;
309 data->selflag = SEL_CHAR;
310
311 if (wp->searchstr != NULL) {
312 data->searchtype = WINDOW_COPY_SEARCHUP;
313 data->searchregex = wp->searchregex;
314 data->searchstr = xstrdup(wp->searchstr);
315 } else {
316 data->searchtype = WINDOW_COPY_OFF;
317 data->searchregex = 0;
318 data->searchstr = NULL;
319 }
320 data->searchmark = NULL;
321 data->searchx = data->searchy = data->searcho = -1;
322 data->timeout = 0;
323
324 data->jumptype = WINDOW_COPY_OFF;
325 data->jumpchar = '\0';
326
327 screen_init(&data->screen, screen_size_x(base), screen_size_y(base), 0);
328 data->modekeys = options_get_number(wp->window->options, "mode-keys");
329
330 evtimer_set(&data->dragtimer, window_copy_scroll_timer, wme);
331
332 return (data);
333}
334
335static struct screen *
336window_copy_init(struct window_mode_entry *wme,
337 __unused struct cmd_find_state *fs, struct args *args)
338{
339 struct window_pane *wp = wme->wp;
340 struct window_copy_mode_data *data;
341 struct screen_write_ctx ctx;
342 u_int i;
343
344 data = window_copy_common_init(wme);
345
346 if (wp->fd != -1 && wp->disabled++ == 0)
347 bufferevent_disable(wp->event, EV_READ|EV_WRITE);
348
349 data->backing = &wp->base;
350 data->cx = data->backing->cx;
351 data->cy = data->backing->cy;
352
353 data->scroll_exit = args_has(args, 'e');
354 data->hide_position = args_has(args, 'H');
355
356 data->screen.cx = data->cx;
357 data->screen.cy = data->cy;
358
359 screen_write_start(&ctx, NULL, &data->screen);
360 for (i = 0; i < screen_size_y(&data->screen); i++)
361 window_copy_write_line(wme, &ctx, i);
362 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
363 screen_write_stop(&ctx);
364
365 return (&data->screen);
366}
367
368static struct screen *
369window_copy_view_init(struct window_mode_entry *wme,
370 __unused struct cmd_find_state *fs, __unused struct args *args)
371{
372 struct window_pane *wp = wme->wp;
373 struct window_copy_mode_data *data;
374 struct screen *base = &wp->base;
375 struct screen *s;
376
377 data = window_copy_common_init(wme);
378
379 data->backing = s = xmalloc(sizeof *data->backing);
380 screen_init(s, screen_size_x(base), screen_size_y(base), UINT_MAX);
381
382 return (&data->screen);
383}
384
385static void
386window_copy_free(struct window_mode_entry *wme)
387{
388 struct window_pane *wp = wme->wp;
389 struct window_copy_mode_data *data = wme->data;
390
391 evtimer_del(&data->dragtimer);
392
393 if (wp->fd != -1 && --wp->disabled == 0)
394 bufferevent_enable(wp->event, EV_READ|EV_WRITE);
395
396 free(data->searchmark);
397 free(data->searchstr);
398
399 if (data->backing != &wp->base) {
400 screen_free(data->backing);
401 free(data->backing);
402 }
403 screen_free(&data->screen);
404
405 free(data);
406}
407
408void
409window_copy_add(struct window_pane *wp, const char *fmt, ...)
410{
411 va_list ap;
412
413 va_start(ap, fmt);
414 window_copy_vadd(wp, fmt, ap);
415 va_end(ap);
416}
417
418void
419window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
420{
421 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
422 struct window_copy_mode_data *data = wme->data;
423 struct screen *backing = data->backing;
424 struct screen_write_ctx back_ctx, ctx;
425 struct grid_cell gc;
426 u_int old_hsize, old_cy;
427
428 if (backing == &wp->base)
429 return;
430
431 memcpy(&gc, &grid_default_cell, sizeof gc);
432
433 old_hsize = screen_hsize(data->backing);
434 screen_write_start(&back_ctx, NULL, backing);
435 if (data->backing_written) {
436 /*
437 * On the second or later line, do a CRLF before writing
438 * (so it's on a new line).
439 */
440 screen_write_carriagereturn(&back_ctx);
441 screen_write_linefeed(&back_ctx, 0, 8);
442 } else
443 data->backing_written = 1;
444 old_cy = backing->cy;
445 screen_write_vnputs(&back_ctx, 0, &gc, fmt, ap);
446 screen_write_stop(&back_ctx);
447
448 data->oy += screen_hsize(data->backing) - old_hsize;
449
450 screen_write_start(&ctx, wp, &data->screen);
451
452 /*
453 * If the history has changed, draw the top line.
454 * (If there's any history at all, it has changed.)
455 */
456 if (screen_hsize(data->backing))
457 window_copy_redraw_lines(wme, 0, 1);
458
459 /* Write the new lines. */
460 window_copy_redraw_lines(wme, old_cy, backing->cy - old_cy + 1);
461
462 screen_write_stop(&ctx);
463}
464
465void
466window_copy_pageup(struct window_pane *wp, int half_page)
467{
468 window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page);
469}
470
471static void
472window_copy_pageup1(struct window_mode_entry *wme, int half_page)
473{
474 struct window_copy_mode_data *data = wme->data;
475 struct screen *s = &data->screen;
476 u_int n, ox, oy, px, py;
477
478 oy = screen_hsize(data->backing) + data->cy - data->oy;
479 ox = window_copy_find_length(wme, oy);
480
481 if (data->cx != ox) {
482 data->lastcx = data->cx;
483 data->lastsx = ox;
484 }
485 data->cx = data->lastcx;
486
487 n = 1;
488 if (screen_size_y(s) > 2) {
489 if (half_page)
490 n = screen_size_y(s) / 2;
491 else
492 n = screen_size_y(s) - 2;
493 }
494
495 if (data->oy + n > screen_hsize(data->backing)) {
496 data->oy = screen_hsize(data->backing);
497 if (data->cy < n)
498 data->cy = 0;
499 else
500 data->cy -= n;
501 } else
502 data->oy += n;
503
504 if (data->screen.sel == NULL || !data->rectflag) {
505 py = screen_hsize(data->backing) + data->cy - data->oy;
506 px = window_copy_find_length(wme, py);
507 if ((data->cx >= data->lastsx && data->cx != px) ||
508 data->cx > px)
509 window_copy_cursor_end_of_line(wme);
510 }
511
512 if (data->searchmark != NULL && !data->timeout)
513 window_copy_search_marks(wme, NULL, data->searchregex);
514 window_copy_update_selection(wme, 1, 0);
515 window_copy_redraw_screen(wme);
516}
517
518static int
519window_copy_pagedown(struct window_mode_entry *wme, int half_page,
520 int scroll_exit)
521{
522 struct window_copy_mode_data *data = wme->data;
523 struct screen *s = &data->screen;
524 u_int n, ox, oy, px, py;
525
526 oy = screen_hsize(data->backing) + data->cy - data->oy;
527 ox = window_copy_find_length(wme, oy);
528
529 if (data->cx != ox) {
530 data->lastcx = data->cx;
531 data->lastsx = ox;
532 }
533 data->cx = data->lastcx;
534
535 n = 1;
536 if (screen_size_y(s) > 2) {
537 if (half_page)
538 n = screen_size_y(s) / 2;
539 else
540 n = screen_size_y(s) - 2;
541 }
542
543 if (data->oy < n) {
544 data->oy = 0;
545 if (data->cy + (n - data->oy) >= screen_size_y(data->backing))
546 data->cy = screen_size_y(data->backing) - 1;
547 else
548 data->cy += n - data->oy;
549 } else
550 data->oy -= n;
551
552 if (data->screen.sel == NULL || !data->rectflag) {
553 py = screen_hsize(data->backing) + data->cy - data->oy;
554 px = window_copy_find_length(wme, py);
555 if ((data->cx >= data->lastsx && data->cx != px) ||
556 data->cx > px)
557 window_copy_cursor_end_of_line(wme);
558 }
559
560 if (scroll_exit && data->oy == 0)
561 return (1);
562 if (data->searchmark != NULL && !data->timeout)
563 window_copy_search_marks(wme, NULL, data->searchregex);
564 window_copy_update_selection(wme, 1, 0);
565 window_copy_redraw_screen(wme);
566 return (0);
567}
568
569static void
570window_copy_previous_paragraph(struct window_mode_entry *wme)
571{
572 struct window_copy_mode_data *data = wme->data;
573 u_int oy;
574
575 oy = screen_hsize(data->backing) + data->cy - data->oy;
576
577 while (oy > 0 && window_copy_find_length(wme, oy) == 0)
578 oy--;
579
580 while (oy > 0 && window_copy_find_length(wme, oy) > 0)
581 oy--;
582
583 window_copy_scroll_to(wme, 0, oy);
584}
585
586static void
587window_copy_next_paragraph(struct window_mode_entry *wme)
588{
589 struct window_copy_mode_data *data = wme->data;
590 struct screen *s = &data->screen;
591 u_int maxy, ox, oy;
592
593 oy = screen_hsize(data->backing) + data->cy - data->oy;
594 maxy = screen_hsize(data->backing) + screen_size_y(s) - 1;
595
596 while (oy < maxy && window_copy_find_length(wme, oy) == 0)
597 oy++;
598
599 while (oy < maxy && window_copy_find_length(wme, oy) > 0)
600 oy++;
601
602 ox = window_copy_find_length(wme, oy);
603 window_copy_scroll_to(wme, ox, oy);
604}
605
606char *
607window_copy_get_word(struct window_pane *wp, u_int x, u_int y)
608{
609 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
610 struct window_copy_mode_data *data = wme->data;
611 struct grid *gd = data->screen.grid;
612
613 return (format_grid_word(gd, x, gd->hsize + y));
614}
615
616char *
617window_copy_get_line(struct window_pane *wp, u_int y)
618{
619 struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
620 struct window_copy_mode_data *data = wme->data;
621 struct grid *gd = data->screen.grid;
622
623 return (format_grid_line(gd, gd->hsize + y));
624}
625
626static void
627window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
628{
629 struct window_copy_mode_data *data = wme->data;
630 struct grid *gd = data->screen.grid;
631 char *s;
632
633 format_add(ft, "scroll_position", "%d", data->oy);
634 format_add(ft, "rectangle_toggle", "%d", data->rectflag);
635
636 format_add(ft, "copy_cursor_x", "%d", data->cx);
637 format_add(ft, "copy_cursor_y", "%d", data->cy);
638
639 format_add(ft, "selection_present", "%d", data->screen.sel != NULL);
640 if (data->screen.sel != NULL) {
641 format_add(ft, "selection_start_x", "%d", data->selx);
642 format_add(ft, "selection_start_y", "%d", data->sely);
643 format_add(ft, "selection_end_x", "%d", data->endselx);
644 format_add(ft, "selection_end_y", "%d", data->endsely);
645 format_add(ft, "selection_active", "%d",
646 data->cursordrag != CURSORDRAG_NONE);
647 } else
648 format_add(ft, "selection_active", "%d", 0);
649
650 s = format_grid_word(gd, data->cx, gd->hsize + data->cy);
651 if (s != NULL) {
652 format_add(ft, "copy_cursor_word", "%s", s);
653 free(s);
654 }
655
656 s = format_grid_line(gd, gd->hsize + data->cy);
657 if (s != NULL) {
658 format_add(ft, "copy_cursor_line", "%s", s);
659 free(s);
660 }
661}
662
663static void
664window_copy_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
665{
666 struct window_pane *wp = wme->wp;
667 struct window_copy_mode_data *data = wme->data;
668 struct screen *s = &data->screen;
669 struct screen_write_ctx ctx;
670 int search;
671
672 screen_resize(s, sx, sy, 1);
673 if (data->backing != &wp->base)
674 screen_resize(data->backing, sx, sy, 1);
675
676 if (data->cy > sy - 1)
677 data->cy = sy - 1;
678 if (data->cx > sx)
679 data->cx = sx;
680 if (data->oy > screen_hsize(data->backing))
681 data->oy = screen_hsize(data->backing);
682
683 search = (data->searchmark != NULL);
684 window_copy_clear_selection(wme);
685 window_copy_clear_marks(wme);
686
687 screen_write_start(&ctx, NULL, s);
688 window_copy_write_lines(wme, &ctx, 0, screen_size_y(s) - 1);
689 screen_write_stop(&ctx);
690
691 if (search && !data->timeout)
692 window_copy_search_marks(wme, NULL, data->searchregex);
693 data->searchx = data->cx;
694 data->searchy = data->cy;
695 data->searcho = data->oy;
696
697 window_copy_redraw_screen(wme);
698}
699
700static const char *
701window_copy_key_table(struct window_mode_entry *wme)
702{
703 struct window_pane *wp = wme->wp;
704
705 if (options_get_number(wp->window->options, "mode-keys") == MODEKEY_VI)
706 return ("copy-mode-vi");
707 return ("copy-mode");
708}
709
710static int
711window_copy_expand_search_string(struct window_copy_cmd_state *cs)
712{
713 struct window_mode_entry *wme = cs->wme;
714 struct window_copy_mode_data *data = wme->data;
715 const char *argument;
716 char *expanded;
717
718 if (cs->args->argc == 2) {
719 argument = cs->args->argv[1];
720 if (*argument != '\0') {
721 if (args_has(cs->args, 'F')) {
722 expanded = format_single(NULL, argument, NULL,
723 NULL, NULL, wme->wp);
724 if (*expanded == '\0') {
725 free(expanded);
726 return (0);
727 }
728 free(data->searchstr);
729 data->searchstr = expanded;
730 } else {
731 free(data->searchstr);
732 data->searchstr = xstrdup(argument);
733 }
734 }
735 }
736 return (1);
737}
738
739static enum window_copy_cmd_action
740window_copy_cmd_append_selection(struct window_copy_cmd_state *cs)
741{
742 struct window_mode_entry *wme = cs->wme;
743 struct session *s = cs->s;
744
745 if (s != NULL)
746 window_copy_append_selection(wme);
747 window_copy_clear_selection(wme);
748 return (WINDOW_COPY_CMD_REDRAW);
749}
750
751static enum window_copy_cmd_action
752window_copy_cmd_append_selection_and_cancel(struct window_copy_cmd_state *cs)
753{
754 struct window_mode_entry *wme = cs->wme;
755 struct session *s = cs->s;
756
757 if (s != NULL)
758 window_copy_append_selection(wme);
759 window_copy_clear_selection(wme);
760 return (WINDOW_COPY_CMD_CANCEL);
761}
762
763static enum window_copy_cmd_action
764window_copy_cmd_back_to_indentation(struct window_copy_cmd_state *cs)
765{
766 struct window_mode_entry *wme = cs->wme;
767
768 window_copy_cursor_back_to_indentation(wme);
769 return (WINDOW_COPY_CMD_NOTHING);
770}
771
772static enum window_copy_cmd_action
773window_copy_cmd_begin_selection(struct window_copy_cmd_state *cs)
774{
775 struct window_mode_entry *wme = cs->wme;
776 struct client *c = cs->c;
777 struct mouse_event *m = cs->m;
778 struct window_copy_mode_data *data = wme->data;
779
780 if (m != NULL) {
781 window_copy_start_drag(c, m);
782 return (WINDOW_COPY_CMD_NOTHING);
783 }
784
785 data->lineflag = LINE_SEL_NONE;
786 data->selflag = SEL_CHAR;
787 window_copy_start_selection(wme);
788 return (WINDOW_COPY_CMD_REDRAW);
789}
790
791static enum window_copy_cmd_action
792window_copy_cmd_stop_selection(struct window_copy_cmd_state *cs)
793{
794 struct window_mode_entry *wme = cs->wme;
795 struct window_copy_mode_data *data = wme->data;
796
797 data->cursordrag = CURSORDRAG_NONE;
798 data->lineflag = LINE_SEL_NONE;
799 data->selflag = SEL_CHAR;
800 return (WINDOW_COPY_CMD_NOTHING);
801}
802
803static enum window_copy_cmd_action
804window_copy_cmd_bottom_line(struct window_copy_cmd_state *cs)
805{
806 struct window_mode_entry *wme = cs->wme;
807 struct window_copy_mode_data *data = wme->data;
808
809 data->cx = 0;
810 data->cy = screen_size_y(&data->screen) - 1;
811
812 window_copy_update_selection(wme, 1, 0);
813 return (WINDOW_COPY_CMD_REDRAW);
814}
815
816static enum window_copy_cmd_action
817window_copy_cmd_cancel(__unused struct window_copy_cmd_state *cs)
818{
819 return (WINDOW_COPY_CMD_CANCEL);
820}
821
822static enum window_copy_cmd_action
823window_copy_cmd_clear_selection(struct window_copy_cmd_state *cs)
824{
825 struct window_mode_entry *wme = cs->wme;
826
827 window_copy_clear_selection(wme);
828 return (WINDOW_COPY_CMD_REDRAW);
829}
830
831static enum window_copy_cmd_action
832window_copy_cmd_copy_end_of_line(struct window_copy_cmd_state *cs)
833{
834 struct window_mode_entry *wme = cs->wme;
835 struct client *c = cs->c;
836 struct session *s = cs->s;
837 struct winlink *wl = cs->wl;
838 struct window_pane *wp = wme->wp;
839 u_int np = wme->prefix;
840 char *prefix = NULL;
841
842 if (cs->args->argc == 2)
843 prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp);
844
845 window_copy_start_selection(wme);
846 for (; np > 1; np--)
847 window_copy_cursor_down(wme, 0);
848 window_copy_cursor_end_of_line(wme);
849
850 if (s != NULL) {
851 window_copy_copy_selection(wme, prefix);
852
853 free(prefix);
854 return (WINDOW_COPY_CMD_CANCEL);
855 }
856
857 free(prefix);
858 return (WINDOW_COPY_CMD_REDRAW);
859}
860
861static enum window_copy_cmd_action
862window_copy_cmd_copy_line(struct window_copy_cmd_state *cs)
863{
864 struct window_mode_entry *wme = cs->wme;
865 struct client *c = cs->c;
866 struct session *s = cs->s;
867 struct winlink *wl = cs->wl;
868 struct window_pane *wp = wme->wp;
869 struct window_copy_mode_data *data = wme->data;
870 u_int np = wme->prefix;
871 char *prefix = NULL;
872
873 if (cs->args->argc == 2)
874 prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp);
875
876 data->selflag = SEL_CHAR;
877 window_copy_cursor_start_of_line(wme);
878 window_copy_start_selection(wme);
879 for (; np > 1; np--)
880 window_copy_cursor_down(wme, 0);
881 window_copy_cursor_end_of_line(wme);
882
883 if (s != NULL) {
884 window_copy_copy_selection(wme, prefix);
885
886 free(prefix);
887 return (WINDOW_COPY_CMD_CANCEL);
888 }
889
890 free(prefix);
891 return (WINDOW_COPY_CMD_REDRAW);
892}
893
894static enum window_copy_cmd_action
895window_copy_cmd_copy_selection_no_clear(struct window_copy_cmd_state *cs)
896{
897 struct window_mode_entry *wme = cs->wme;
898 struct client *c = cs->c;
899 struct session *s = cs->s;
900 struct winlink *wl = cs->wl;
901 struct window_pane *wp = wme->wp;
902 char *prefix = NULL;
903
904 if (cs->args->argc == 2)
905 prefix = format_single(NULL, cs->args->argv[1], c, s, wl, wp);
906
907 if (s != NULL)
908 window_copy_copy_selection(wme, prefix);
909
910 free(prefix);
911 return (WINDOW_COPY_CMD_NOTHING);
912}
913
914static enum window_copy_cmd_action
915window_copy_cmd_copy_selection(struct window_copy_cmd_state *cs)
916{
917 struct window_mode_entry *wme = cs->wme;
918
919 window_copy_cmd_copy_selection_no_clear(cs);
920 window_copy_clear_selection(wme);
921 return (WINDOW_COPY_CMD_REDRAW);
922}
923
924static enum window_copy_cmd_action
925window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
926{
927 struct window_mode_entry *wme = cs->wme;
928
929 window_copy_cmd_copy_selection_no_clear(cs);
930 window_copy_clear_selection(wme);
931 return (WINDOW_COPY_CMD_CANCEL);
932}
933
934static enum window_copy_cmd_action
935window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
936{
937 struct window_mode_entry *wme = cs->wme;
938 u_int np = wme->prefix;
939
940 for (; np != 0; np--)
941 window_copy_cursor_down(wme, 0);
942 return (WINDOW_COPY_CMD_NOTHING);
943}
944
945static enum window_copy_cmd_action
946window_copy_cmd_cursor_down_and_cancel(struct window_copy_cmd_state *cs)
947{
948 struct window_mode_entry *wme = cs->wme;
949 struct window_copy_mode_data *data = wme->data;
950 u_int np = wme->prefix, cy;
951
952 cy = data->cy;
953 for (; np != 0; np--)
954 window_copy_cursor_down(wme, 0);
955 if (cy == data->cy && data->oy == 0)
956 return (WINDOW_COPY_CMD_CANCEL);
957 return (WINDOW_COPY_CMD_NOTHING);
958}
959
960static enum window_copy_cmd_action
961window_copy_cmd_cursor_left(struct window_copy_cmd_state *cs)
962{
963 struct window_mode_entry *wme = cs->wme;
964 u_int np = wme->prefix;
965
966 for (; np != 0; np--)
967 window_copy_cursor_left(wme);
968 return (WINDOW_COPY_CMD_NOTHING);
969}
970
971static enum window_copy_cmd_action
972window_copy_cmd_cursor_right(struct window_copy_cmd_state *cs)
973{
974 struct window_mode_entry *wme = cs->wme;
975 u_int np = wme->prefix;
976
977 for (; np != 0; np--)
978 window_copy_cursor_right(wme);
979 return (WINDOW_COPY_CMD_NOTHING);
980}
981
982static enum window_copy_cmd_action
983window_copy_cmd_cursor_up(struct window_copy_cmd_state *cs)
984{
985 struct window_mode_entry *wme = cs->wme;
986 u_int np = wme->prefix;
987
988 for (; np != 0; np--)
989 window_copy_cursor_up(wme, 0);
990 return (WINDOW_COPY_CMD_NOTHING);
991}
992
993static enum window_copy_cmd_action
994window_copy_cmd_end_of_line(struct window_copy_cmd_state *cs)
995{
996 struct window_mode_entry *wme = cs->wme;
997
998 window_copy_cursor_end_of_line(wme);
999 return (WINDOW_COPY_CMD_NOTHING);
1000}
1001
1002static enum window_copy_cmd_action
1003window_copy_cmd_halfpage_down(struct window_copy_cmd_state *cs)
1004{
1005 struct window_mode_entry *wme = cs->wme;
1006 struct window_copy_mode_data *data = wme->data;
1007 u_int np = wme->prefix;
1008
1009 for (; np != 0; np--) {
1010 if (window_copy_pagedown(wme, 1, data->scroll_exit))
1011 return (WINDOW_COPY_CMD_CANCEL);
1012 }
1013 return (WINDOW_COPY_CMD_NOTHING);
1014}
1015
1016static enum window_copy_cmd_action
1017window_copy_cmd_halfpage_down_and_cancel(struct window_copy_cmd_state *cs)
1018{
1019
1020 struct window_mode_entry *wme = cs->wme;
1021 u_int np = wme->prefix;
1022
1023 for (; np != 0; np--) {
1024 if (window_copy_pagedown(wme, 1, 1))
1025 return (WINDOW_COPY_CMD_CANCEL);
1026 }
1027 return (WINDOW_COPY_CMD_NOTHING);
1028}
1029
1030static enum window_copy_cmd_action
1031window_copy_cmd_halfpage_up(struct window_copy_cmd_state *cs)
1032{
1033 struct window_mode_entry *wme = cs->wme;
1034 u_int np = wme->prefix;
1035
1036 for (; np != 0; np--)
1037 window_copy_pageup1(wme, 1);
1038 return (WINDOW_COPY_CMD_NOTHING);
1039}
1040
1041static enum window_copy_cmd_action
1042window_copy_cmd_history_bottom(struct window_copy_cmd_state *cs)
1043{
1044 struct window_mode_entry *wme = cs->wme;
1045 struct window_copy_mode_data *data = wme->data;
1046 u_int oy;
1047
1048 oy = screen_hsize(data->backing) + data->cy - data->oy;
1049 if (data->lineflag == LINE_SEL_RIGHT_LEFT && oy == data->endsely)
1050 window_copy_other_end(wme);
1051
1052 data->cy = screen_size_y(&data->screen) - 1;
1053 data->cx = window_copy_find_length(wme, data->cy);
1054 data->oy = 0;
1055
1056 if (data->searchmark != NULL && !data->timeout)
1057 window_copy_search_marks(wme, NULL, data->searchregex);
1058 window_copy_update_selection(wme, 1, 0);
1059 return (WINDOW_COPY_CMD_REDRAW);
1060}
1061
1062static enum window_copy_cmd_action
1063window_copy_cmd_history_top(struct window_copy_cmd_state *cs)
1064{
1065 struct window_mode_entry *wme = cs->wme;
1066 struct window_copy_mode_data *data = wme->data;
1067 u_int oy;
1068
1069 oy = screen_hsize(data->backing) + data->cy - data->oy;
1070 if (data->lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
1071 window_copy_other_end(wme);
1072
1073 data->cy = 0;
1074 data->cx = 0;
1075 data->oy = screen_hsize(data->backing);
1076
1077 if (data->searchmark != NULL && !data->timeout)
1078 window_copy_search_marks(wme, NULL, data->searchregex);
1079 window_copy_update_selection(wme, 1, 0);
1080 return (WINDOW_COPY_CMD_REDRAW);
1081}
1082
1083static enum window_copy_cmd_action
1084window_copy_cmd_jump_again(struct window_copy_cmd_state *cs)
1085{
1086 struct window_mode_entry *wme = cs->wme;
1087 struct window_copy_mode_data *data = wme->data;
1088 u_int np = wme->prefix;
1089
1090 switch (data->jumptype) {
1091 case WINDOW_COPY_JUMPFORWARD:
1092 for (; np != 0; np--)
1093 window_copy_cursor_jump(wme);
1094 break;
1095 case WINDOW_COPY_JUMPBACKWARD:
1096 for (; np != 0; np--)
1097 window_copy_cursor_jump_back(wme);
1098 break;
1099 case WINDOW_COPY_JUMPTOFORWARD:
1100 for (; np != 0; np--)
1101 window_copy_cursor_jump_to(wme);
1102 break;
1103 case WINDOW_COPY_JUMPTOBACKWARD:
1104 for (; np != 0; np--)
1105 window_copy_cursor_jump_to_back(wme);
1106 break;
1107 }
1108 return (WINDOW_COPY_CMD_NOTHING);
1109}
1110
1111static enum window_copy_cmd_action
1112window_copy_cmd_jump_reverse(struct window_copy_cmd_state *cs)
1113{
1114 struct window_mode_entry *wme = cs->wme;
1115 struct window_copy_mode_data *data = wme->data;
1116 u_int np = wme->prefix;
1117
1118 switch (data->jumptype) {
1119 case WINDOW_COPY_JUMPFORWARD:
1120 for (; np != 0; np--)
1121 window_copy_cursor_jump_back(wme);
1122 break;
1123 case WINDOW_COPY_JUMPBACKWARD:
1124 for (; np != 0; np--)
1125 window_copy_cursor_jump(wme);
1126 break;
1127 case WINDOW_COPY_JUMPTOFORWARD:
1128 for (; np != 0; np--)
1129 window_copy_cursor_jump_to_back(wme);
1130 break;
1131 case WINDOW_COPY_JUMPTOBACKWARD:
1132 for (; np != 0; np--)
1133 window_copy_cursor_jump_to(wme);
1134 break;
1135 }
1136 return (WINDOW_COPY_CMD_NOTHING);
1137}
1138
1139static enum window_copy_cmd_action
1140window_copy_cmd_middle_line(struct window_copy_cmd_state *cs)
1141{
1142 struct window_mode_entry *wme = cs->wme;
1143 struct window_copy_mode_data *data = wme->data;
1144
1145 data->cx = 0;
1146 data->cy = (screen_size_y(&data->screen) - 1) / 2;
1147
1148 window_copy_update_selection(wme, 1, 0);
1149 return (WINDOW_COPY_CMD_REDRAW);
1150}
1151
1152static enum window_copy_cmd_action
1153window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs)
1154{
1155 struct window_mode_entry *wme = cs->wme;
1156 u_int np = wme->prefix;
1157 struct window_copy_mode_data *data = wme->data;
1158 struct screen *s = data->backing;
1159 char open[] = "{[(", close[] = "}])";
1160 char tried, found, start, *cp;
1161 u_int px, py, xx, n;
1162 struct grid_cell gc;
1163 int failed;
1164
1165 for (; np != 0; np--) {
1166 /* Get cursor position and line length. */
1167 px = data->cx;
1168 py = screen_hsize(s) + data->cy - data->oy;
1169 xx = window_copy_find_length(wme, py);
1170 if (xx == 0)
1171 break;
1172
1173 /*
1174 * Get the current character. If not on a bracket, try the
1175 * previous. If still not, then behave like previous-word.
1176 */
1177 tried = 0;
1178 retry:
1179 grid_get_cell(s->grid, px, py, &gc);
1180 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1181 cp = NULL;
1182 else {
1183 found = *gc.data.data;
1184 cp = strchr(close, found);
1185 }
1186 if (cp == NULL) {
1187 if (data->modekeys == MODEKEY_EMACS) {
1188 if (!tried && px > 0) {
1189 px--;
1190 tried = 1;
1191 goto retry;
1192 }
1193 window_copy_cursor_previous_word(wme, "}]) ", 1);
1194 }
1195 continue;
1196 }
1197 start = open[cp - close];
1198
1199 /* Walk backward until the matching bracket is reached. */
1200 n = 1;
1201 failed = 0;
1202 do {
1203 if (px == 0) {
1204 if (py == 0) {
1205 failed = 1;
1206 break;
1207 }
1208 do {
1209 py--;
1210 xx = window_copy_find_length(wme, py);
1211 } while (xx == 0 && py > 0);
1212 if (xx == 0 && py == 0) {
1213 failed = 1;
1214 break;
1215 }
1216 px = xx - 1;
1217 } else
1218 px--;
1219
1220 grid_get_cell(s->grid, px, py, &gc);
1221 if (gc.data.size == 1 &&
1222 (~gc.flags & GRID_FLAG_PADDING)) {
1223 if (*gc.data.data == found)
1224 n++;
1225 else if (*gc.data.data == start)
1226 n--;
1227 }
1228 } while (n != 0);
1229
1230 /* Move the cursor to the found location if any. */
1231 if (!failed)
1232 window_copy_scroll_to(wme, px, py);
1233 }
1234
1235 return (WINDOW_COPY_CMD_NOTHING);
1236}
1237
1238static enum window_copy_cmd_action
1239window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs)
1240{
1241 struct window_mode_entry *wme = cs->wme;
1242 u_int np = wme->prefix;
1243 struct window_copy_mode_data *data = wme->data;
1244 struct screen *s = data->backing;
1245 char open[] = "{[(", close[] = "}])";
1246 char tried, found, end, *cp;
1247 u_int px, py, xx, yy, sx, sy, n;
1248 struct grid_cell gc;
1249 int failed;
1250 struct grid_line *gl;
1251
1252 for (; np != 0; np--) {
1253 /* Get cursor position and line length. */
1254 px = data->cx;
1255 py = screen_hsize(s) + data->cy - data->oy;
1256 xx = window_copy_find_length(wme, py);
1257 yy = screen_hsize(s) + screen_size_y(s) - 1;
1258 if (xx == 0)
1259 break;
1260
1261 /*
1262 * Get the current character. If not on a bracket, try the
1263 * next. If still not, then behave like next-word.
1264 */
1265 tried = 0;
1266 retry:
1267 grid_get_cell(s->grid, px, py, &gc);
1268 if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING))
1269 cp = NULL;
1270 else {
1271 found = *gc.data.data;
1272
1273 /*
1274 * In vi mode, attempt to move to previous bracket if a
1275 * closing bracket is found first. If this fails,
1276 * return to the original cursor position.
1277 */
1278 cp = strchr(close, found);
1279 if (cp != NULL && data->modekeys == MODEKEY_VI) {
1280 sx = data->cx;
1281 sy = screen_hsize(s) + data->cy - data->oy;
1282
1283 window_copy_scroll_to(wme, px, py);
1284 window_copy_cmd_previous_matching_bracket(cs);
1285
1286 px = data->cx;
1287 py = screen_hsize(s) + data->cy - data->oy;
1288 grid_get_cell(s->grid, px, py, &gc);
1289 if (gc.data.size != 1 ||
1290 (gc.flags & GRID_FLAG_PADDING) ||
1291 strchr(close, *gc.data.data) == NULL)
1292 window_copy_scroll_to(wme, sx, sy);
1293 break;
1294 }
1295
1296 cp = strchr(open, found);
1297 }
1298 if (cp == NULL) {
1299 if (data->modekeys == MODEKEY_EMACS) {
1300 if (!tried && px <= xx) {
1301 px++;
1302 tried = 1;
1303 goto retry;
1304 }
1305 window_copy_cursor_next_word_end(wme, "{[( ",
1306 0);
1307 continue;
1308 }
1309 /* For vi, continue searching for bracket until EOL. */
1310 if (px > xx) {
1311 if (py == yy)
1312 continue;
1313 gl = grid_get_line(s->grid, py);
1314 if (~gl->flags & GRID_LINE_WRAPPED)
1315 continue;
1316 if (gl->cellsize > s->grid->sx)
1317 continue;
1318 px = 0;
1319 py++;
1320 xx = window_copy_find_length(wme, py);
1321 } else
1322 px++;
1323 goto retry;
1324 }
1325 end = close[cp - open];
1326
1327 /* Walk forward until the matching bracket is reached. */
1328 n = 1;
1329 failed = 0;
1330 do {
1331 if (px > xx) {
1332 if (py == yy) {
1333 failed = 1;
1334 break;
1335 }
1336 px = 0;
1337 py++;
1338 xx = window_copy_find_length(wme, py);
1339 } else
1340 px++;
1341
1342 grid_get_cell(s->grid, px, py, &gc);
1343 if (gc.data.size == 1 &&
1344 (~gc.flags & GRID_FLAG_PADDING)) {
1345 if (*gc.data.data == found)
1346 n++;
1347 else if (*gc.data.data == end)
1348 n--;
1349 }
1350 } while (n != 0);
1351
1352 /* Move the cursor to the found location if any. */
1353 if (!failed)
1354 window_copy_scroll_to(wme, px, py);
1355 }
1356
1357 return (WINDOW_COPY_CMD_NOTHING);
1358}
1359
1360static enum window_copy_cmd_action
1361window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs)
1362{
1363 struct window_mode_entry *wme = cs->wme;
1364 u_int np = wme->prefix;
1365
1366 for (; np != 0; np--)
1367 window_copy_next_paragraph(wme);
1368 return (WINDOW_COPY_CMD_NOTHING);
1369}
1370
1371static enum window_copy_cmd_action
1372window_copy_cmd_next_space(struct window_copy_cmd_state *cs)
1373{
1374 struct window_mode_entry *wme = cs->wme;
1375 u_int np = wme->prefix;
1376
1377 for (; np != 0; np--)
1378 window_copy_cursor_next_word(wme, " ");
1379 return (WINDOW_COPY_CMD_NOTHING);
1380}
1381
1382static enum window_copy_cmd_action
1383window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs)
1384{
1385 struct window_mode_entry *wme = cs->wme;
1386 u_int np = wme->prefix;
1387
1388 for (; np != 0; np--)
1389 window_copy_cursor_next_word_end(wme, " ", 0);
1390 return (WINDOW_COPY_CMD_NOTHING);
1391}
1392
1393static enum window_copy_cmd_action
1394window_copy_cmd_next_word(struct window_copy_cmd_state *cs)
1395{
1396 struct window_mode_entry *wme = cs->wme;
1397 struct session *s = cs->s;
1398 u_int np = wme->prefix;
1399 const char *ws;
1400
1401 ws = options_get_string(s->options, "word-separators");
1402 for (; np != 0; np--)
1403 window_copy_cursor_next_word(wme, ws);
1404 return (WINDOW_COPY_CMD_NOTHING);
1405}
1406
1407static enum window_copy_cmd_action
1408window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs)
1409{
1410 struct window_mode_entry *wme = cs->wme;
1411 struct session *s = cs->s;
1412 u_int np = wme->prefix;
1413 const char *ws;
1414
1415 ws = options_get_string(s->options, "word-separators");
1416 for (; np != 0; np--)
1417 window_copy_cursor_next_word_end(wme, ws, 0);
1418 return (WINDOW_COPY_CMD_NOTHING);
1419}
1420
1421static enum window_copy_cmd_action
1422window_copy_cmd_other_end(struct window_copy_cmd_state *cs)
1423{
1424 struct window_mode_entry *wme = cs->wme;
1425 u_int np = wme->prefix;
1426 struct window_copy_mode_data *data = wme->data;
1427
1428 data->selflag = SEL_CHAR;
1429 if ((np % 2) != 0)
1430 window_copy_other_end(wme);
1431 return (WINDOW_COPY_CMD_NOTHING);
1432}
1433
1434static enum window_copy_cmd_action
1435window_copy_cmd_page_down(struct window_copy_cmd_state *cs)
1436{
1437 struct window_mode_entry *wme = cs->wme;
1438 struct window_copy_mode_data *data = wme->data;
1439 u_int np = wme->prefix;
1440
1441 for (; np != 0; np--) {
1442 if (window_copy_pagedown(wme, 0, data->scroll_exit))
1443 return (WINDOW_COPY_CMD_CANCEL);
1444 }
1445 return (WINDOW_COPY_CMD_NOTHING);
1446}
1447
1448static enum window_copy_cmd_action
1449window_copy_cmd_page_down_and_cancel(struct window_copy_cmd_state *cs)
1450{
1451 struct window_mode_entry *wme = cs->wme;
1452 u_int np = wme->prefix;
1453
1454 for (; np != 0; np--) {
1455 if (window_copy_pagedown(wme, 0, 1))
1456 return (WINDOW_COPY_CMD_CANCEL);
1457 }
1458 return (WINDOW_COPY_CMD_NOTHING);
1459}
1460
1461static enum window_copy_cmd_action
1462window_copy_cmd_page_up(struct window_copy_cmd_state *cs)
1463{
1464 struct window_mode_entry *wme = cs->wme;
1465 u_int np = wme->prefix;
1466
1467 for (; np != 0; np--)
1468 window_copy_pageup1(wme, 0);
1469 return (WINDOW_COPY_CMD_NOTHING);
1470}
1471
1472static enum window_copy_cmd_action
1473window_copy_cmd_previous_paragraph(struct window_copy_cmd_state *cs)
1474{
1475 struct window_mode_entry *wme = cs->wme;
1476 u_int np = wme->prefix;
1477
1478 for (; np != 0; np--)
1479 window_copy_previous_paragraph(wme);
1480 return (WINDOW_COPY_CMD_NOTHING);
1481}
1482
1483static enum window_copy_cmd_action
1484window_copy_cmd_previous_space(struct window_copy_cmd_state *cs)
1485{
1486 struct window_mode_entry *wme = cs->wme;
1487 u_int np = wme->prefix;
1488
1489 for (; np != 0; np--)
1490 window_copy_cursor_previous_word(wme, " ", 1);
1491 return (WINDOW_COPY_CMD_NOTHING);
1492}
1493
1494static enum window_copy_cmd_action
1495window_copy_cmd_previous_word(struct window_copy_cmd_state *cs)
1496{
1497 struct window_mode_entry *wme = cs->wme;
1498 struct session *s = cs->s;
1499 u_int np = wme->prefix;
1500 const char *ws;
1501
1502 ws = options_get_string(s->options, "word-separators");
1503 for (; np != 0; np--)
1504 window_copy_cursor_previous_word(wme, ws, 1);
1505 return (WINDOW_COPY_CMD_NOTHING);
1506}
1507
1508static enum window_copy_cmd_action
1509window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs)
1510{
1511 struct window_mode_entry *wme = cs->wme;
1512 struct window_copy_mode_data *data = wme->data;
1513
1514 data->lineflag = LINE_SEL_NONE;
1515 window_copy_rectangle_toggle(wme);
1516
1517 return (WINDOW_COPY_CMD_NOTHING);
1518}
1519
1520static enum window_copy_cmd_action
1521window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs)
1522{
1523 struct window_mode_entry *wme = cs->wme;
1524 struct window_copy_mode_data *data = wme->data;
1525 u_int np = wme->prefix;
1526
1527 for (; np != 0; np--)
1528 window_copy_cursor_down(wme, 1);
1529 if (data->scroll_exit && data->oy == 0)
1530 return (WINDOW_COPY_CMD_CANCEL);
1531 return (WINDOW_COPY_CMD_NOTHING);
1532}
1533
1534static enum window_copy_cmd_action
1535window_copy_cmd_scroll_down_and_cancel(struct window_copy_cmd_state *cs)
1536{
1537 struct window_mode_entry *wme = cs->wme;
1538 struct window_copy_mode_data *data = wme->data;
1539 u_int np = wme->prefix;
1540
1541 for (; np != 0; np--)
1542 window_copy_cursor_down(wme, 1);
1543 if (data->oy == 0)
1544 return (WINDOW_COPY_CMD_CANCEL);
1545 return (WINDOW_COPY_CMD_NOTHING);
1546}
1547
1548static enum window_copy_cmd_action
1549window_copy_cmd_scroll_up(struct window_copy_cmd_state *cs)
1550{
1551 struct window_mode_entry *wme = cs->wme;
1552 u_int np = wme->prefix;
1553
1554 for (; np != 0; np--)
1555 window_copy_cursor_up(wme, 1);
1556 return (WINDOW_COPY_CMD_NOTHING);
1557}
1558
1559static enum window_copy_cmd_action
1560window_copy_cmd_search_again(struct window_copy_cmd_state *cs)
1561{
1562 struct window_mode_entry *wme = cs->wme;
1563 struct window_copy_mode_data *data = wme->data;
1564 u_int np = wme->prefix;
1565
1566 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
1567 for (; np != 0; np--)
1568 window_copy_search_up(wme, data->searchregex);
1569 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
1570 for (; np != 0; np--)
1571 window_copy_search_down(wme, data->searchregex);
1572 }
1573 return (WINDOW_COPY_CMD_NOTHING);
1574}
1575
1576static enum window_copy_cmd_action
1577window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs)
1578{
1579 struct window_mode_entry *wme = cs->wme;
1580 struct window_copy_mode_data *data = wme->data;
1581 u_int np = wme->prefix;
1582
1583 if (data->searchtype == WINDOW_COPY_SEARCHUP) {
1584 for (; np != 0; np--)
1585 window_copy_search_down(wme, data->searchregex);
1586 } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) {
1587 for (; np != 0; np--)
1588 window_copy_search_up(wme, data->searchregex);
1589 }
1590 return (WINDOW_COPY_CMD_NOTHING);
1591}
1592
1593static enum window_copy_cmd_action
1594window_copy_cmd_select_line(struct window_copy_cmd_state *cs)
1595{
1596 struct window_mode_entry *wme = cs->wme;
1597 struct window_copy_mode_data *data = wme->data;
1598 u_int np = wme->prefix;
1599
1600 data->lineflag = LINE_SEL_LEFT_RIGHT;
1601 data->rectflag = 0;
1602 data->selflag = SEL_LINE;
1603 data->dx = data->cx;
1604 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
1605
1606 window_copy_cursor_start_of_line(wme);
1607 data->selrx = data->cx;
1608 data->selry = screen_hsize(data->backing) + data->cy - data->oy;
1609 data->endselrx = window_copy_find_length(wme, data->selry);
1610 data->endselry = data->selry;
1611 window_copy_start_selection(wme);
1612 for (; np > 1; np--)
1613 window_copy_cursor_down(wme, 0);
1614 window_copy_cursor_end_of_line(wme);
1615
1616 return (WINDOW_COPY_CMD_REDRAW);
1617}
1618
1619static enum window_copy_cmd_action
1620window_copy_cmd_select_word(struct window_copy_cmd_state *cs)
1621{
1622 struct window_mode_entry *wme = cs->wme;
1623 struct session *s = cs->s;
1624 struct window_copy_mode_data *data = wme->data;
1625 u_int px, py;
1626
1627 data->lineflag = LINE_SEL_LEFT_RIGHT;
1628 data->rectflag = 0;
1629 data->selflag = SEL_WORD;
1630 data->dx = data->cx;
1631 data->dy = screen_hsize(data->backing) + data->cy - data->oy;
1632
1633 data->ws = options_get_string(s->options, "word-separators");
1634 window_copy_cursor_previous_word(wme, data->ws, 0);
1635 px = data->cx;
1636 py = screen_hsize(data->backing) + data->cy - data->oy;
1637 data->selrx = px;
1638 data->selry = py;
1639 window_copy_start_selection(wme);
1640
1641 if (px >= window_copy_find_length(wme, py) ||
1642 !window_copy_in_set(wme, px + 1, py, data->ws))
1643 window_copy_cursor_next_word_end(wme, data->ws, 1);
1644 else {
1645 window_copy_update_cursor(wme, px, data->cy);
1646 if (window_copy_update_selection(wme, 1, 1))
1647 window_copy_redraw_lines(wme, data->cy, 1);
1648 }
1649 data->endselrx = data->cx;
1650 data->endselry = screen_hsize(data->backing) + data->cy - data->oy;
1651 if (data->dx > data->endselrx)
1652 data->dx = data->endselrx;
1653
1654 return (WINDOW_COPY_CMD_REDRAW);
1655}
1656
1657static enum window_copy_cmd_action
1658window_copy_cmd_start_of_line(struct window_copy_cmd_state *cs)
1659{
1660 struct window_mode_entry *wme = cs->wme;
1661
1662 window_copy_cursor_start_of_line(wme);
1663 return (WINDOW_COPY_CMD_NOTHING);
1664}
1665
1666static enum window_copy_cmd_action
1667window_copy_cmd_top_line(struct window_copy_cmd_state *cs)
1668{
1669 struct window_mode_entry *wme = cs->wme;
1670 struct window_copy_mode_data *data = wme->data;
1671
1672 data->cx = 0;
1673 data->cy = 0;
1674
1675 window_copy_update_selection(wme, 1, 0);
1676 return (WINDOW_COPY_CMD_REDRAW);
1677}
1678
1679static enum window_copy_cmd_action
1680window_copy_cmd_copy_pipe_no_clear(struct window_copy_cmd_state *cs)
1681{
1682 struct window_mode_entry *wme = cs->wme;
1683 struct client *c = cs->c;
1684 struct session *s = cs->s;
1685 struct winlink *wl = cs->wl;
1686 struct window_pane *wp = wme->wp;
1687 char *command = NULL;
1688 char *prefix = NULL;
1689
1690 if (cs->args->argc == 3)
1691 prefix = format_single(NULL, cs->args->argv[2], c, s, wl, wp);
1692
1693 if (s != NULL && *cs->args->argv[1] != '\0') {
1694 command = format_single(NULL, cs->args->argv[1], c, s, wl, wp);
1695 window_copy_copy_pipe(wme, s, prefix, command);
1696 free(command);
1697 }
1698
1699 free(prefix);
1700 return (WINDOW_COPY_CMD_NOTHING);
1701}
1702
1703static enum window_copy_cmd_action
1704window_copy_cmd_copy_pipe(struct window_copy_cmd_state *cs)
1705{
1706 struct window_mode_entry *wme = cs->wme;
1707
1708 window_copy_cmd_copy_pipe_no_clear(cs);
1709 window_copy_clear_selection(wme);
1710 return (WINDOW_COPY_CMD_REDRAW);
1711}
1712
1713static enum window_copy_cmd_action
1714window_copy_cmd_copy_pipe_and_cancel(struct window_copy_cmd_state *cs)
1715{
1716 struct window_mode_entry *wme = cs->wme;
1717
1718 window_copy_cmd_copy_pipe_no_clear(cs);
1719 window_copy_clear_selection(wme);
1720 return (WINDOW_COPY_CMD_CANCEL);
1721}
1722
1723static enum window_copy_cmd_action
1724window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
1725{
1726 struct window_mode_entry *wme = cs->wme;
1727 const char *argument = cs->args->argv[1];
1728
1729 if (*argument != '\0')
1730 window_copy_goto_line(wme, argument);
1731 return (WINDOW_COPY_CMD_NOTHING);
1732}
1733
1734static enum window_copy_cmd_action
1735window_copy_cmd_jump_backward(struct window_copy_cmd_state *cs)
1736{
1737 struct window_mode_entry *wme = cs->wme;
1738 struct window_copy_mode_data *data = wme->data;
1739 u_int np = wme->prefix;
1740 const char *argument = cs->args->argv[1];
1741
1742 if (*argument != '\0') {
1743 data->jumptype = WINDOW_COPY_JUMPBACKWARD;
1744 data->jumpchar = *argument;
1745 for (; np != 0; np--)
1746 window_copy_cursor_jump_back(wme);
1747 }
1748 return (WINDOW_COPY_CMD_NOTHING);
1749}
1750
1751static enum window_copy_cmd_action
1752window_copy_cmd_jump_forward(struct window_copy_cmd_state *cs)
1753{
1754 struct window_mode_entry *wme = cs->wme;
1755 struct window_copy_mode_data *data = wme->data;
1756 u_int np = wme->prefix;
1757 const char *argument = cs->args->argv[1];
1758
1759 if (*argument != '\0') {
1760 data->jumptype = WINDOW_COPY_JUMPFORWARD;
1761 data->jumpchar = *argument;
1762 for (; np != 0; np--)
1763 window_copy_cursor_jump(wme);
1764 }
1765 return (WINDOW_COPY_CMD_NOTHING);
1766}
1767
1768static enum window_copy_cmd_action
1769window_copy_cmd_jump_to_backward(struct window_copy_cmd_state *cs)
1770{
1771 struct window_mode_entry *wme = cs->wme;
1772 struct window_copy_mode_data *data = wme->data;
1773 u_int np = wme->prefix;
1774 const char *argument = cs->args->argv[1];
1775
1776 if (*argument != '\0') {
1777 data->jumptype = WINDOW_COPY_JUMPTOBACKWARD;
1778 data->jumpchar = *argument;
1779 for (; np != 0; np--)
1780 window_copy_cursor_jump_to_back(wme);
1781 }
1782 return (WINDOW_COPY_CMD_NOTHING);
1783}
1784
1785static enum window_copy_cmd_action
1786window_copy_cmd_jump_to_forward(struct window_copy_cmd_state *cs)
1787{
1788 struct window_mode_entry *wme = cs->wme;
1789 struct window_copy_mode_data *data = wme->data;
1790 u_int np = wme->prefix;
1791 const char *argument = cs->args->argv[1];
1792
1793 if (*argument != '\0') {
1794 data->jumptype = WINDOW_COPY_JUMPTOFORWARD;
1795 data->jumpchar = *argument;
1796 for (; np != 0; np--)
1797 window_copy_cursor_jump_to(wme);
1798 }
1799 return (WINDOW_COPY_CMD_NOTHING);
1800}
1801
1802static enum window_copy_cmd_action
1803window_copy_cmd_search_backward(struct window_copy_cmd_state *cs)
1804{
1805 struct window_mode_entry *wme = cs->wme;
1806 struct window_copy_mode_data *data = wme->data;
1807 u_int np = wme->prefix;
1808
1809 if (!window_copy_expand_search_string(cs))
1810 return (WINDOW_COPY_CMD_NOTHING);
1811
1812 if (data->searchstr != NULL) {
1813 data->searchtype = WINDOW_COPY_SEARCHUP;
1814 data->searchregex = 1;
1815 data->timeout = 0;
1816 for (; np != 0; np--)
1817 window_copy_search_up(wme, 1);
1818 }
1819 return (WINDOW_COPY_CMD_NOTHING);
1820}
1821
1822static enum window_copy_cmd_action
1823window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs)
1824{
1825 struct window_mode_entry *wme = cs->wme;
1826 struct window_copy_mode_data *data = wme->data;
1827 u_int np = wme->prefix;
1828
1829 if (!window_copy_expand_search_string(cs))
1830 return (WINDOW_COPY_CMD_NOTHING);
1831
1832 if (data->searchstr != NULL) {
1833 data->searchtype = WINDOW_COPY_SEARCHUP;
1834 data->searchregex = 0;
1835 data->timeout = 0;
1836 for (; np != 0; np--)
1837 window_copy_search_up(wme, 0);
1838 }
1839 return (WINDOW_COPY_CMD_NOTHING);
1840}
1841
1842static enum window_copy_cmd_action
1843window_copy_cmd_search_forward(struct window_copy_cmd_state *cs)
1844{
1845 struct window_mode_entry *wme = cs->wme;
1846 struct window_copy_mode_data *data = wme->data;
1847 u_int np = wme->prefix;
1848
1849 if (!window_copy_expand_search_string(cs))
1850 return (WINDOW_COPY_CMD_NOTHING);
1851
1852 if (data->searchstr != NULL) {
1853 data->searchtype = WINDOW_COPY_SEARCHDOWN;
1854 data->searchregex = 1;
1855 data->timeout = 0;
1856 for (; np != 0; np--)
1857 window_copy_search_down(wme, 1);
1858 }
1859 return (WINDOW_COPY_CMD_NOTHING);
1860}
1861
1862static enum window_copy_cmd_action
1863window_copy_cmd_search_forward_text(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 u_int np = wme->prefix;
1868
1869 if (!window_copy_expand_search_string(cs))
1870 return (WINDOW_COPY_CMD_NOTHING);
1871
1872 if (data->searchstr != NULL) {
1873 data->searchtype = WINDOW_COPY_SEARCHDOWN;
1874 data->searchregex = 0;
1875 data->timeout = 0;
1876 for (; np != 0; np--)
1877 window_copy_search_down(wme, 0);
1878 }
1879 return (WINDOW_COPY_CMD_NOTHING);
1880}
1881
1882static enum window_copy_cmd_action
1883window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs)
1884{
1885 struct window_mode_entry *wme = cs->wme;
1886 struct window_copy_mode_data *data = wme->data;
1887 const char *argument = cs->args->argv[1];
1888 const char *ss = data->searchstr;
1889 char prefix;
1890 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
1891
1892 data->timeout = 0;
1893
1894 prefix = *argument++;
1895 if (data->searchx == -1 || data->searchy == -1) {
1896 data->searchx = data->cx;
1897 data->searchy = data->cy;
1898 data->searcho = data->oy;
1899 } else if (ss != NULL && strcmp(argument, ss) != 0) {
1900 data->cx = data->searchx;
1901 data->cy = data->searchy;
1902 data->oy = data->searcho;
1903 action = WINDOW_COPY_CMD_REDRAW;
1904 }
1905 if (*argument == '\0') {
1906 window_copy_clear_marks(wme);
1907 return (WINDOW_COPY_CMD_REDRAW);
1908 }
1909 switch (prefix) {
1910 case '=':
1911 case '-':
1912 data->searchtype = WINDOW_COPY_SEARCHUP;
1913 data->searchregex = 0;
1914 free(data->searchstr);
1915 data->searchstr = xstrdup(argument);
1916 if (!window_copy_search_up(wme, 0)) {
1917 window_copy_clear_marks(wme);
1918 return (WINDOW_COPY_CMD_REDRAW);
1919 }
1920 break;
1921 case '+':
1922 data->searchtype = WINDOW_COPY_SEARCHDOWN;
1923 data->searchregex = 0;
1924 free(data->searchstr);
1925 data->searchstr = xstrdup(argument);
1926 if (!window_copy_search_down(wme, 0)) {
1927 window_copy_clear_marks(wme);
1928 return (WINDOW_COPY_CMD_REDRAW);
1929 }
1930 break;
1931 }
1932 return (action);
1933}
1934
1935static enum window_copy_cmd_action
1936window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs)
1937{
1938 struct window_mode_entry *wme = cs->wme;
1939 struct window_copy_mode_data *data = wme->data;
1940 const char *argument = cs->args->argv[1];
1941 const char *ss = data->searchstr;
1942 char prefix;
1943 enum window_copy_cmd_action action = WINDOW_COPY_CMD_NOTHING;
1944
1945 data->timeout = 0;
1946
1947 prefix = *argument++;
1948 if (data->searchx == -1 || data->searchy == -1) {
1949 data->searchx = data->cx;
1950 data->searchy = data->cy;
1951 data->searcho = data->oy;
1952 } else if (ss != NULL && strcmp(argument, ss) != 0) {
1953 data->cx = data->searchx;
1954 data->cy = data->searchy;
1955 data->oy = data->searcho;
1956 action = WINDOW_COPY_CMD_REDRAW;
1957 }
1958 if (*argument == '\0') {
1959 window_copy_clear_marks(wme);
1960 return (WINDOW_COPY_CMD_REDRAW);
1961 }
1962 switch (prefix) {
1963 case '=':
1964 case '+':
1965 data->searchtype = WINDOW_COPY_SEARCHDOWN;
1966 data->searchregex = 0;
1967 free(data->searchstr);
1968 data->searchstr = xstrdup(argument);
1969 if (!window_copy_search_down(wme, 0)) {
1970 window_copy_clear_marks(wme);
1971 return (WINDOW_COPY_CMD_REDRAW);
1972 }
1973 break;
1974 case '-':
1975 data->searchtype = WINDOW_COPY_SEARCHUP;
1976 data->searchregex = 0;
1977 free(data->searchstr);
1978 data->searchstr = xstrdup(argument);
1979 if (!window_copy_search_up(wme, 0)) {
1980 window_copy_clear_marks(wme);
1981 return (WINDOW_COPY_CMD_REDRAW);
1982 }
1983 }
1984 return (action);
1985}
1986
1987static const struct {
1988 const char *command;
1989 int minargs;
1990 int maxargs;
1991 int ismotion;
1992 enum window_copy_cmd_action (*f)(struct window_copy_cmd_state *);
1993} window_copy_cmd_table[] = {
1994 { "append-selection", 0, 0, 0,
1995 window_copy_cmd_append_selection },
1996 { "append-selection-and-cancel", 0, 0, 0,
1997 window_copy_cmd_append_selection_and_cancel },
1998 { "back-to-indentation", 0, 0, 0,
1999 window_copy_cmd_back_to_indentation },
2000 { "begin-selection", 0, 0, 0,
2001 window_copy_cmd_begin_selection },
2002 { "bottom-line", 0, 0, 1,
2003 window_copy_cmd_bottom_line },
2004 { "cancel", 0, 0, 0,
2005 window_copy_cmd_cancel },
2006 { "clear-selection", 0, 0, 0,
2007 window_copy_cmd_clear_selection },
2008 { "copy-end-of-line", 0, 1, 0,
2009 window_copy_cmd_copy_end_of_line },
2010 { "copy-line", 0, 1, 0,
2011 window_copy_cmd_copy_line },
2012 { "copy-pipe-no-clear", 1, 2, 0,
2013 window_copy_cmd_copy_pipe_no_clear },
2014 { "copy-pipe", 1, 2, 0,
2015 window_copy_cmd_copy_pipe },
2016 { "copy-pipe-and-cancel", 1, 2, 0,
2017 window_copy_cmd_copy_pipe_and_cancel },
2018 { "copy-selection-no-clear", 0, 1, 0,
2019 window_copy_cmd_copy_selection_no_clear },
2020 { "copy-selection", 0, 1, 0,
2021 window_copy_cmd_copy_selection },
2022 { "copy-selection-and-cancel", 0, 1, 0,
2023 window_copy_cmd_copy_selection_and_cancel },
2024 { "cursor-down", 0, 0, 1,
2025 window_copy_cmd_cursor_down },
2026 { "cursor-down-and-cancel", 0, 0, 0,
2027 window_copy_cmd_cursor_down_and_cancel },
2028 { "cursor-left", 0, 0, 1,
2029 window_copy_cmd_cursor_left },
2030 { "cursor-right", 0, 0, 1,
2031 window_copy_cmd_cursor_right },
2032 { "cursor-up", 0, 0, 1,
2033 window_copy_cmd_cursor_up },
2034 { "end-of-line", 0, 0, 1,
2035 window_copy_cmd_end_of_line },
2036 { "goto-line", 1, 1, 1,
2037 window_copy_cmd_goto_line },
2038 { "halfpage-down", 0, 0, 1,
2039 window_copy_cmd_halfpage_down },
2040 { "halfpage-down-and-cancel", 0, 0, 0,
2041 window_copy_cmd_halfpage_down_and_cancel },
2042 { "halfpage-up", 0, 0, 1,
2043 window_copy_cmd_halfpage_up },
2044 { "history-bottom", 0, 0, 1,
2045 window_copy_cmd_history_bottom },
2046 { "history-top", 0, 0, 1,
2047 window_copy_cmd_history_top },
2048 { "jump-again", 0, 0, 1,
2049 window_copy_cmd_jump_again },
2050 { "jump-backward", 1, 1, 1,
2051 window_copy_cmd_jump_backward },
2052 { "jump-forward", 1, 1, 1,
2053 window_copy_cmd_jump_forward },
2054 { "jump-reverse", 0, 0, 1,
2055 window_copy_cmd_jump_reverse },
2056 { "jump-to-backward", 1, 1, 1,
2057 window_copy_cmd_jump_to_backward },
2058 { "jump-to-forward", 1, 1, 1,
2059 window_copy_cmd_jump_to_forward },
2060 { "middle-line", 0, 0, 1,
2061 window_copy_cmd_middle_line },
2062 { "next-matching-bracket", 0, 0, 0,
2063 window_copy_cmd_next_matching_bracket },
2064 { "next-paragraph", 0, 0, 1,
2065 window_copy_cmd_next_paragraph },
2066 { "next-space", 0, 0, 1,
2067 window_copy_cmd_next_space },
2068 { "next-space-end", 0, 0, 1,
2069 window_copy_cmd_next_space_end },
2070 { "next-word", 0, 0, 1,
2071 window_copy_cmd_next_word },
2072 { "next-word-end", 0, 0, 1,
2073 window_copy_cmd_next_word_end },
2074 { "other-end", 0, 0, 1,
2075 window_copy_cmd_other_end },
2076 { "page-down", 0, 0, 1,
2077 window_copy_cmd_page_down },
2078 { "page-down-and-cancel", 0, 0, 0,
2079 window_copy_cmd_page_down_and_cancel },
2080 { "page-up", 0, 0, 1,
2081 window_copy_cmd_page_up },
2082 { "previous-matching-bracket", 0, 0, 0,
2083 window_copy_cmd_previous_matching_bracket },
2084 { "previous-paragraph", 0, 0, 1,
2085 window_copy_cmd_previous_paragraph },
2086 { "previous-space", 0, 0, 1,
2087 window_copy_cmd_previous_space },
2088 { "previous-word", 0, 0, 1,
2089 window_copy_cmd_previous_word },
2090 { "rectangle-toggle", 0, 0, 0,
2091 window_copy_cmd_rectangle_toggle },
2092 { "scroll-down", 0, 0, 1,
2093 window_copy_cmd_scroll_down },
2094 { "scroll-down-and-cancel", 0, 0, 0,
2095 window_copy_cmd_scroll_down_and_cancel },
2096 { "scroll-up", 0, 0, 1,
2097 window_copy_cmd_scroll_up },
2098 { "search-again", 0, 0, 0,
2099 window_copy_cmd_search_again },
2100 { "search-backward", 0, 1, 0,
2101 window_copy_cmd_search_backward },
2102 { "search-backward-text", 0, 1, 0,
2103 window_copy_cmd_search_backward_text },
2104 { "search-backward-incremental", 1, 1, 0,
2105 window_copy_cmd_search_backward_incremental },
2106 { "search-forward", 0, 1, 0,
2107 window_copy_cmd_search_forward },
2108 { "search-forward-text", 0, 1, 0,
2109 window_copy_cmd_search_forward_text },
2110 { "search-forward-incremental", 1, 1, 0,
2111 window_copy_cmd_search_forward_incremental },
2112 { "search-reverse", 0, 0, 0,
2113 window_copy_cmd_search_reverse },
2114 { "select-line", 0, 0, 0,
2115 window_copy_cmd_select_line },
2116 { "select-word", 0, 0, 0,
2117 window_copy_cmd_select_word },
2118 { "start-of-line", 0, 0, 1,
2119 window_copy_cmd_start_of_line },
2120 { "stop-selection", 0, 0, 0,
2121 window_copy_cmd_stop_selection },
2122 { "top-line", 0, 0, 1,
2123 window_copy_cmd_top_line },
2124};
2125
2126static void
2127window_copy_command(struct window_mode_entry *wme, struct client *c,
2128 struct session *s, struct winlink *wl, struct args *args,
2129 struct mouse_event *m)
2130{
2131 struct window_copy_mode_data *data = wme->data;
2132 struct window_copy_cmd_state cs;
2133 enum window_copy_cmd_action action;
2134 const char *command;
2135 u_int i;
2136 int ismotion = 0, keys;
2137
2138 if (args->argc == 0)
2139 return;
2140 command = args->argv[0];
2141
2142 if (m != NULL && m->valid && !MOUSE_WHEEL(m->b))
2143 window_copy_move_mouse(m);
2144
2145 cs.wme = wme;
2146 cs.args = args;
2147 cs.m = m;
2148
2149 cs.c = c;
2150 cs.s = s;
2151 cs.wl = wl;
2152
2153 action = WINDOW_COPY_CMD_NOTHING;
2154 for (i = 0; i < nitems(window_copy_cmd_table); i++) {
2155 if (strcmp(window_copy_cmd_table[i].command, command) == 0) {
2156 if (args->argc - 1 < window_copy_cmd_table[i].minargs ||
2157 args->argc - 1 > window_copy_cmd_table[i].maxargs)
2158 break;
2159 ismotion = window_copy_cmd_table[i].ismotion;
2160 action = window_copy_cmd_table[i].f (&cs);
2161 break;
2162 }
2163 }
2164
2165 if (strncmp(command, "search-", 7) != 0 && data->searchmark != NULL) {
2166 keys = options_get_number(wme->wp->window->options, "mode-keys");
2167 if (keys != MODEKEY_VI || !ismotion) {
2168 window_copy_clear_marks(wme);
2169 data->searchx = data->searchy = -1;
2170 } else if (data->searchthis != -1) {
2171 data->searchthis = -1;
2172 action = WINDOW_COPY_CMD_REDRAW;
2173 }
2174 if (action == WINDOW_COPY_CMD_NOTHING)
2175 action = WINDOW_COPY_CMD_REDRAW;
2176 }
2177 wme->prefix = 1;
2178
2179 if (action == WINDOW_COPY_CMD_CANCEL)
2180 window_pane_reset_mode(wme->wp);
2181 else if (action == WINDOW_COPY_CMD_REDRAW)
2182 window_copy_redraw_screen(wme);
2183}
2184
2185static void
2186window_copy_scroll_to(struct window_mode_entry *wme, u_int px, u_int py)
2187{
2188 struct window_copy_mode_data *data = wme->data;
2189 struct grid *gd = data->backing->grid;
2190 u_int offset, gap;
2191
2192 data->cx = px;
2193
2194 if (py >= gd->hsize - data->oy && py < gd->hsize - data->oy + gd->sy)
2195 data->cy = py - (gd->hsize - data->oy);
2196 else {
2197 gap = gd->sy / 4;
2198 if (py < gd->sy) {
2199 offset = 0;
2200 data->cy = py;
2201 } else if (py > gd->hsize + gd->sy - gap) {
2202 offset = gd->hsize;
2203 data->cy = py - gd->hsize;
2204 } else {
2205 offset = py + gap - gd->sy;
2206 data->cy = py - offset;
2207 }
2208 data->oy = gd->hsize - offset;
2209 }
2210
2211 if (data->searchmark != NULL && !data->timeout)
2212 window_copy_search_marks(wme, NULL, data->searchregex);
2213 window_copy_update_selection(wme, 1, 0);
2214 window_copy_redraw_screen(wme);
2215}
2216
2217static int
2218window_copy_search_compare(struct grid *gd, u_int px, u_int py,
2219 struct grid *sgd, u_int spx, int cis)
2220{
2221 struct grid_cell gc, sgc;
2222 const struct utf8_data *ud, *sud;
2223
2224 grid_get_cell(gd, px, py, &gc);
2225 ud = &gc.data;
2226 grid_get_cell(sgd, spx, 0, &sgc);
2227 sud = &sgc.data;
2228
2229 if (ud->size != sud->size || ud->width != sud->width)
2230 return (0);
2231
2232 if (cis && ud->size == 1)
2233 return (tolower(ud->data[0]) == sud->data[0]);
2234
2235 return (memcmp(ud->data, sud->data, ud->size) == 0);
2236}
2237
2238static int
2239window_copy_search_lr(struct grid *gd,
2240 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
2241{
2242 u_int ax, bx, px, pywrap, endline;
2243 int matched;
2244 struct grid_line *gl;
2245
2246 endline = gd->hsize + gd->sy - 1;
2247 for (ax = first; ax < last; ax++) {
2248 for (bx = 0; bx < sgd->sx; bx++) {
2249 px = ax + bx;
2250 pywrap = py;
2251 /* Wrap line. */
2252 while (px >= gd->sx && pywrap < endline) {
2253 gl = grid_get_line(gd, pywrap);
2254 if (~gl->flags & GRID_LINE_WRAPPED)
2255 break;
2256 px -= gd->sx;
2257 pywrap++;
2258 }
2259 /* We have run off the end of the grid. */
2260 if (px >= gd->sx)
2261 break;
2262 matched = window_copy_search_compare(gd, px, pywrap,
2263 sgd, bx, cis);
2264 if (!matched)
2265 break;
2266 }
2267 if (bx == sgd->sx) {
2268 *ppx = ax;
2269 return (1);
2270 }
2271 }
2272 return (0);
2273}
2274
2275static int
2276window_copy_search_rl(struct grid *gd,
2277 struct grid *sgd, u_int *ppx, u_int py, u_int first, u_int last, int cis)
2278{
2279 u_int ax, bx, px, pywrap, endline;
2280 int matched;
2281 struct grid_line *gl;
2282
2283 endline = gd->hsize + gd->sy - 1;
2284 for (ax = last; ax > first; ax--) {
2285 for (bx = 0; bx < sgd->sx; bx++) {
2286 px = ax - 1 + bx;
2287 pywrap = py;
2288 /* Wrap line. */
2289 while (px >= gd->sx && pywrap < endline) {
2290 gl = grid_get_line(gd, pywrap);
2291 if (~gl->flags & GRID_LINE_WRAPPED)
2292 break;
2293 px -= gd->sx;
2294 pywrap++;
2295 }
2296 /* We have run off the end of the grid. */
2297 if (px >= gd->sx)
2298 break;
2299 matched = window_copy_search_compare(gd, px, pywrap,
2300 sgd, bx, cis);
2301 if (!matched)
2302 break;
2303 }
2304 if (bx == sgd->sx) {
2305 *ppx = ax - 1;
2306 return (1);
2307 }
2308 }
2309 return (0);
2310}
2311
2312static int
2313window_copy_search_lr_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
2314 u_int first, u_int last, regex_t *reg)
2315{
2316 int eflags = 0;
2317 u_int endline, foundx, foundy, len, pywrap, size = 1;
2318 char *buf;
2319 regmatch_t regmatch;
2320 struct grid_line *gl;
2321
2322 /*
2323 * This can happen during search if the last match was the last
2324 * character on a line.
2325 */
2326 if (first >= last)
2327 return (0);
2328
2329 /* Set flags for regex search. */
2330 if (first != 0)
2331 eflags |= REG_NOTBOL;
2332
2333 /* Need to look at the entire string. */
2334 buf = xmalloc(size);
2335 buf[0] = '\0';
2336 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
2337 len = gd->sx - first;
2338 endline = gd->hsize + gd->sy - 1;
2339 pywrap = py;
2340 while (buf != NULL && pywrap <= endline) {
2341 gl = grid_get_line(gd, pywrap);
2342 if (~gl->flags & GRID_LINE_WRAPPED)
2343 break;
2344 pywrap++;
2345 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
2346 len += gd->sx;
2347 }
2348
2349 if (regexec(reg, buf, 1, &regmatch, eflags) == 0) {
2350 foundx = first;
2351 foundy = py;
2352 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
2353 buf + regmatch.rm_so);
2354 if (foundy == py && foundx < last) {
2355 *ppx = foundx;
2356 len -= foundx - first;
2357 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
2358 buf + regmatch.rm_eo);
2359 *psx = foundx;
2360 while (foundy > py) {
2361 *psx += gd->sx;
2362 foundy--;
2363 }
2364 *psx -= *ppx;
2365 free(buf);
2366 return (1);
2367 }
2368 }
2369
2370 free(buf);
2371 *ppx = 0;
2372 *psx = 0;
2373 return (0);
2374}
2375
2376static int
2377window_copy_search_rl_regex(struct grid *gd, u_int *ppx, u_int *psx, u_int py,
2378 u_int first, u_int last, regex_t *reg)
2379{
2380 int eflags = 0;
2381 u_int endline, len, pywrap, size = 1;
2382 char *buf;
2383 struct grid_line *gl;
2384
2385 /* Set flags for regex search. */
2386 if (first != 0)
2387 eflags |= REG_NOTBOL;
2388
2389 /* Need to look at the entire string. */
2390 buf = xmalloc(size);
2391 buf[0] = '\0';
2392 buf = window_copy_stringify(gd, py, first, gd->sx, buf, &size);
2393 len = gd->sx - first;
2394 endline = gd->hsize + gd->sy - 1;
2395 pywrap = py;
2396 while (buf != NULL && (pywrap <= endline)) {
2397 gl = grid_get_line(gd, pywrap);
2398 if (~gl->flags & GRID_LINE_WRAPPED)
2399 break;
2400 pywrap++;
2401 buf = window_copy_stringify(gd, pywrap, 0, gd->sx, buf, &size);
2402 len += gd->sx;
2403 }
2404
2405 if (window_copy_last_regex(gd, py, first, last, len, ppx, psx, buf,
2406 reg, eflags))
2407 {
2408 free(buf);
2409 return (1);
2410 }
2411
2412 free(buf);
2413 *ppx = 0;
2414 *psx = 0;
2415 return (0);
2416}
2417
2418static const char *
2419window_copy_cellstring(const struct grid_line *gl, u_int px, size_t *size)
2420{
2421 struct grid_cell_entry *gce;
2422
2423 if (px >= gl->cellsize) {
2424 *size = 1;
2425 return (" ");
2426 }
2427
2428 gce = &gl->celldata[px];
2429 if (~gce->flags & GRID_FLAG_EXTENDED) {
2430 *size = 1;
2431 return (&gce->data.data);
2432 }
2433
2434 *size = gl->extddata[gce->offset].data.size;
2435 return (gl->extddata[gce->offset].data.data);
2436}
2437
2438/* Find last match in given range. */
2439static int
2440window_copy_last_regex(struct grid *gd, u_int py, u_int first, u_int last,
2441 u_int len, u_int *ppx, u_int *psx, const char *buf, const regex_t *preg,
2442 int eflags)
2443{
2444 u_int foundx, foundy, oldx, px = 0, savepx, savesx = 0;
2445 regmatch_t regmatch;
2446
2447 foundx = first;
2448 foundy = py;
2449 oldx = first;
2450 while (regexec(preg, buf + px, 1, &regmatch, eflags) == 0) {
2451 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
2452 buf + px + regmatch.rm_so);
2453 if (foundy > py || foundx >= last)
2454 break;
2455 len -= foundx - oldx;
2456 savepx = foundx;
2457 window_copy_cstrtocellpos(gd, len, &foundx, &foundy,
2458 buf + px + regmatch.rm_eo);
2459 if (foundy > py || foundx >= last) {
2460 *ppx = savepx;
2461 *psx = foundx;
2462 while (foundy > py) {
2463 *psx += gd->sx;
2464 foundy--;
2465 }
2466 *psx -= *ppx;
2467 return (1);
2468 } else {
2469 savesx = foundx - savepx;
2470 len -= savesx;
2471 oldx = foundx;
2472 }
2473 px += regmatch.rm_eo;
2474 }
2475
2476 if (savesx > 0) {
2477 *ppx = savepx;
2478 *psx = savesx;
2479 return (1);
2480 } else {
2481 *ppx = 0;
2482 *psx = 0;
2483 return (0);
2484 }
2485}
2486
2487/* Stringify line and append to input buffer. Caller frees. */
2488static char *
2489window_copy_stringify(struct grid *gd, u_int py, u_int first, u_int last,
2490 char *buf, u_int *size)
2491{
2492 u_int ax, bx, newsize = *size;
2493 const struct grid_line *gl;
2494 const char *d;
2495 size_t bufsize = 1024, dlen;
2496
2497 while (bufsize < newsize)
2498 bufsize *= 2;
2499 buf = xrealloc(buf, bufsize);
2500
2501 gl = grid_peek_line(gd, py);
2502 bx = *size - 1;
2503 for (ax = first; ax < last; ax++) {
2504 d = window_copy_cellstring(gl, ax, &dlen);
2505 newsize += dlen;
2506 while (bufsize < newsize) {
2507 bufsize *= 2;
2508 buf = xrealloc(buf, bufsize);
2509 }
2510 if (dlen == 1)
2511 buf[bx++] = *d;
2512 else {
2513 memcpy(buf + bx, d, dlen);
2514 bx += dlen;
2515 }
2516 }
2517 buf[newsize - 1] = '\0';
2518
2519 *size = newsize;
2520 return (buf);
2521}
2522
2523/* Map start of C string containing UTF-8 data to grid cell position. */
2524static void
2525window_copy_cstrtocellpos(struct grid *gd, u_int ncells, u_int *ppx, u_int *ppy,
2526 const char *str)
2527{
2528 u_int cell, ccell, px, pywrap, pos, len;
2529 int match;
2530 const struct grid_line *gl;
2531 const char *d;
2532 size_t dlen;
2533 struct {
2534 const char *d;
2535 size_t dlen;
2536 } *cells;
2537
2538 /* Populate the array of cell data. */
2539 cells = xreallocarray(NULL, ncells, sizeof cells[0]);
2540 cell = 0;
2541 px = *ppx;
2542 pywrap = *ppy;
2543 gl = grid_peek_line(gd, pywrap);
2544 while (cell < ncells) {
2545 cells[cell].d = window_copy_cellstring(gl, px,
2546 &cells[cell].dlen);
2547 cell++;
2548 px++;
2549 if (px == gd->sx) {
2550 px = 0;
2551 pywrap++;
2552 gl = grid_peek_line(gd, pywrap);
2553 }
2554 }
2555
2556 /* Locate starting cell. */
2557 cell = 0;
2558 len = strlen(str);
2559 while (cell < ncells) {
2560 ccell = cell;
2561 pos = 0;
2562 match = 1;
2563 while (ccell < ncells) {
2564 if (str[pos] == '\0') {
2565 match = 0;
2566 break;
2567 }
2568 d = cells[ccell].d;
2569 dlen = cells[ccell].dlen;
2570 if (dlen == 1) {
2571 if (str[pos] != *d) {
2572 match = 0;
2573 break;
2574 }
2575 pos++;
2576 } else {
2577 if (dlen > len - pos)
2578 dlen = len - pos;
2579 if (memcmp(str + pos, d, dlen) != 0) {
2580 match = 0;
2581 break;
2582 }
2583 pos += dlen;
2584 }
2585 ccell++;
2586 }
2587 if (match)
2588 break;
2589 cell++;
2590 }
2591
2592 /* If not found this will be one past the end. */
2593 px = *ppx + cell;
2594 pywrap = *ppy;
2595 while (px >= gd->sx) {
2596 px -= gd->sx;
2597 pywrap++;
2598 }
2599
2600 *ppx = px;
2601 *ppy = pywrap;
2602
2603 /* Free cell data. */
2604 free(cells);
2605}
2606
2607static void
2608window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
2609{
2610 if (*fx == 0) { /* left */
2611 if (*fy == 0) { /* top */
2612 if (wrapflag) {
2613 *fx = screen_size_x(s) - 1;
2614 *fy = screen_hsize(s) + screen_size_y(s) - 1;
2615 }
2616 return;
2617 }
2618 *fx = screen_size_x(s) - 1;
2619 *fy = *fy - 1;
2620 } else
2621 *fx = *fx - 1;
2622}
2623
2624static void
2625window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag)
2626{
2627 if (*fx == screen_size_x(s) - 1) { /* right */
2628 if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */
2629 if (wrapflag) {
2630 *fx = 0;
2631 *fy = 0;
2632 }
2633 return;
2634 }
2635 *fx = 0;
2636 *fy = *fy + 1;
2637 } else
2638 *fx = *fx + 1;
2639}
2640
2641static int
2642window_copy_is_lowercase(const char *ptr)
2643{
2644 while (*ptr != '\0') {
2645 if (*ptr != tolower((u_char)*ptr))
2646 return (0);
2647 ++ptr;
2648 }
2649 return (1);
2650}
2651
2652/*
2653 * Search for text stored in sgd starting from position fx,fy up to endline. If
2654 * found, jump to it. If cis then ignore case. The direction is 0 for searching
2655 * up, down otherwise. If wrap then go to begin/end of grid and try again if
2656 * not found.
2657 */
2658static int
2659window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd,
2660 struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap,
2661 int direction, int regex)
2662{
2663 u_int i, px, sx, ssize = 1;
2664 int found = 0, cflags = REG_EXTENDED;
2665 char *sbuf;
2666 regex_t reg;
2667
2668 if (regex) {
2669 sbuf = xmalloc(ssize);
2670 sbuf[0] = '\0';
2671 sbuf = window_copy_stringify(sgd, 0, 0, sgd->sx, sbuf, &ssize);
2672 if (cis)
2673 cflags |= REG_ICASE;
2674 if (regcomp(&reg, sbuf, cflags) != 0) {
2675 free(sbuf);
2676 return (0);
2677 }
2678 }
2679
2680 if (direction) {
2681 for (i = fy; i <= endline; i++) {
2682 if (regex) {
2683 found = window_copy_search_lr_regex(gd,
2684 &px, &sx, i, fx, gd->sx, &reg);
2685 } else {
2686 found = window_copy_search_lr(gd, sgd,
2687 &px, i, fx, gd->sx, cis);
2688 }
2689 if (found)
2690 break;
2691 fx = 0;
2692 }
2693 } else {
2694 for (i = fy + 1; endline < i; i--) {
2695 if (regex) {
2696 found = window_copy_search_rl_regex(gd,
2697 &px, &sx, i - 1, 0, fx + 1, &reg);
2698 } else {
2699 found = window_copy_search_rl(gd, sgd,
2700 &px, i - 1, 0, fx + 1, cis);
2701 }
2702 if (found) {
2703 i--;
2704 break;
2705 }
2706 fx = gd->sx - 1;
2707 }
2708 }
2709 if (regex) {
2710 free(sbuf);
2711 regfree(&reg);
2712 }
2713
2714 if (found) {
2715 window_copy_scroll_to(wme, px, i);
2716 return (1);
2717 }
2718 if (wrap) {
2719 return (window_copy_search_jump(wme, gd, sgd,
2720 direction ? 0 : gd->sx - 1,
2721 direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0,
2722 direction, regex));
2723 }
2724 return (0);
2725}
2726
2727/*
2728 * Search in for text searchstr. If direction is 0 then search up, otherwise
2729 * down.
2730 */
2731static int
2732window_copy_search(struct window_mode_entry *wme, int direction, int regex)
2733{
2734 struct window_pane *wp = wme->wp;
2735 struct window_copy_mode_data *data = wme->data;
2736 struct screen *s = data->backing, ss;
2737 struct screen_write_ctx ctx;
2738 struct grid *gd = s->grid;
2739 const char *str = data->searchstr;
2740 u_int fx, fy, endline;
2741 int wrapflag, cis, found;
2742
2743 if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0')
2744 regex = 0;
2745
2746 if (data->timeout)
2747 return (0);
2748
2749 free(wp->searchstr);
2750 wp->searchstr = xstrdup(str);
2751 wp->searchregex = regex;
2752
2753 fx = data->cx;
2754 fy = screen_hsize(data->backing) - data->oy + data->cy;
2755
2756 screen_init(&ss, screen_write_strlen("%s", str), 1, 0);
2757 screen_write_start(&ctx, NULL, &ss);
2758 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s", str);
2759 screen_write_stop(&ctx);
2760
2761 wrapflag = options_get_number(wp->window->options, "wrap-search");
2762 cis = window_copy_is_lowercase(str);
2763
2764 if (direction) {
2765 window_copy_move_right(s, &fx, &fy, wrapflag);
2766 endline = gd->hsize + gd->sy - 1;
2767 } else {
2768 window_copy_move_left(s, &fx, &fy, wrapflag);
2769 endline = 0;
2770 }
2771
2772 found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis,
2773 wrapflag, direction, regex);
2774
2775 if (window_copy_search_marks(wme, &ss, regex))
2776 window_copy_redraw_screen(wme);
2777
2778 screen_free(&ss);
2779 return (found);
2780}
2781
2782static int
2783window_copy_search_marks(struct window_mode_entry *wme, struct screen *ssp,
2784 int regex)
2785{
2786 struct window_copy_mode_data *data = wme->data;
2787 struct screen *s = data->backing, ss;
2788 struct screen_write_ctx ctx;
2789 struct grid *gd = s->grid;
2790 const struct grid_line *gl;
2791 int found, cis, which = -1;
2792 int cflags = REG_EXTENDED;
2793 u_int px, py, b, nfound = 0, width;
2794 u_int ssize = 1;
2795 char *sbuf;
2796 regex_t reg;
2797 time_t tstart, t;
2798
2799 if (ssp == NULL) {
2800 width = screen_write_strlen("%s", data->searchstr);
2801 screen_init(&ss, width, 1, 0);
2802 screen_write_start(&ctx, NULL, &ss);
2803 screen_write_nputs(&ctx, -1, &grid_default_cell, "%s",
2804 data->searchstr);
2805 screen_write_stop(&ctx);
2806 ssp = &ss;
2807 } else
2808 width = screen_size_x(ssp);
2809
2810 cis = window_copy_is_lowercase(data->searchstr);
2811
2812 free(data->searchmark);
2813 data->searchmark = bit_alloc((gd->hsize + gd->sy) * gd->sx);
2814
2815 if (regex) {
2816 sbuf = xmalloc(ssize);
2817 sbuf[0] = '\0';
2818 sbuf = window_copy_stringify(ssp->grid, 0, 0, ssp->grid->sx,
2819 sbuf, &ssize);
2820 if (cis)
2821 cflags |= REG_ICASE;
2822 if (regcomp(&reg, sbuf, cflags) != 0) {
2823 free(sbuf);
2824 return (0);
2825 }
2826 }
2827 time(&tstart);
2828 for (py = gd->hsize - data->oy; py > 0; py--) {
2829 gl = grid_peek_line(gd, py - 1);
2830 if (~gl->flags & GRID_LINE_WRAPPED)
2831 break;
2832 }
2833 for (; py < gd->hsize - data->oy + gd->sy; py++) {
2834 px = 0;
2835 for (;;) {
2836 if (regex) {
2837 found = window_copy_search_lr_regex(gd,
2838 &px, &width, py, px, gd->sx, &reg);
2839 if (!found)
2840 break;
2841 } else {
2842 found = window_copy_search_lr(gd, ssp->grid,
2843 &px, py, px, gd->sx, cis);
2844 if (!found)
2845 break;
2846 }
2847
2848 nfound++;
2849 if (px == data->cx &&
2850 py == gd->hsize + data->cy - data->oy)
2851 which = nfound;
2852
2853 b = (py * gd->sx) + px;
2854 bit_nset(data->searchmark, b, b + width - 1);
2855
2856 px++;
2857 }
2858
2859 time(&t);
2860 if (t - tstart > WINDOW_COPY_SEARCH_TIMEOUT) {
2861 data->timeout = 1;
2862 break;
2863 }
2864 }
2865 if (regex) {
2866 free(sbuf);
2867 regfree(&reg);
2868 }
2869 if (data->timeout) {
2870 window_copy_clear_marks(wme);
2871 return (1);
2872 }
2873
2874 if (which != -1)
2875 data->searchthis = 1 + nfound - which;
2876 else
2877 data->searchthis = -1;
2878 data->searchcount = nfound;
2879
2880 if (ssp == &ss)
2881 screen_free(&ss);
2882 return (1);
2883}
2884
2885static void
2886window_copy_clear_marks(struct window_mode_entry *wme)
2887{
2888 struct window_copy_mode_data *data = wme->data;
2889
2890 free(data->searchmark);
2891 data->searchmark = NULL;
2892}
2893
2894static int
2895window_copy_search_up(struct window_mode_entry *wme, int regex)
2896{
2897 return (window_copy_search(wme, 0, regex));
2898}
2899
2900static int
2901window_copy_search_down(struct window_mode_entry *wme, int regex)
2902{
2903 return (window_copy_search(wme, 1, regex));
2904}
2905
2906static void
2907window_copy_goto_line(struct window_mode_entry *wme, const char *linestr)
2908{
2909 struct window_copy_mode_data *data = wme->data;
2910 const char *errstr;
2911 int lineno;
2912
2913 lineno = strtonum(linestr, -1, INT_MAX, &errstr);
2914 if (errstr != NULL)
2915 return;
2916 if (lineno < 0 || (u_int)lineno > screen_hsize(data->backing))
2917 lineno = screen_hsize(data->backing);
2918
2919 data->oy = lineno;
2920 window_copy_update_selection(wme, 1, 0);
2921 window_copy_redraw_screen(wme);
2922}
2923
2924static void
2925window_copy_write_line(struct window_mode_entry *wme,
2926 struct screen_write_ctx *ctx, u_int py)
2927{
2928 struct window_pane *wp = wme->wp;
2929 struct window_copy_mode_data *data = wme->data;
2930 struct screen *s = &data->screen;
2931 struct options *oo = wp->window->options;
2932 struct grid_cell gc;
2933 char hdr[512];
2934 size_t size = 0;
2935
2936 style_apply(&gc, oo, "mode-style");
2937 gc.flags |= GRID_FLAG_NOPALETTE;
2938
2939 if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
2940 if (data->searchmark == NULL) {
2941 if (data->timeout) {
2942 size = xsnprintf(hdr, sizeof hdr,
2943 "(timed out) [%u/%u]", data->oy,
2944 screen_hsize(data->backing));
2945 } else {
2946 size = xsnprintf(hdr, sizeof hdr,
2947 "[%u/%u]", data->oy,
2948 screen_hsize(data->backing));
2949 }
2950 } else {
2951 if (data->searchthis == -1) {
2952 size = xsnprintf(hdr, sizeof hdr,
2953 "(%u results) [%d/%u]", data->searchcount,
2954 data->oy, screen_hsize(data->backing));
2955 } else {
2956 size = xsnprintf(hdr, sizeof hdr,
2957 "(%u/%u results) [%d/%u]", data->searchthis,
2958 data->searchcount, data->oy,
2959 screen_hsize(data->backing));
2960 }
2961 }
2962 if (size > screen_size_x(s))
2963 size = screen_size_x(s);
2964 screen_write_cursormove(ctx, screen_size_x(s) - size, 0, 0);
2965 screen_write_puts(ctx, &gc, "%s", hdr);
2966 } else
2967 size = 0;
2968
2969 if (size < screen_size_x(s)) {
2970 screen_write_cursormove(ctx, 0, py, 0);
2971 screen_write_copy(ctx, data->backing, 0,
2972 (screen_hsize(data->backing) - data->oy) + py,
2973 screen_size_x(s) - size, 1, data->searchmark, &gc);
2974 }
2975
2976 if (py == data->cy && data->cx == screen_size_x(s)) {
2977 memcpy(&gc, &grid_default_cell, sizeof gc);
2978 screen_write_cursormove(ctx, screen_size_x(s) - 1, py, 0);
2979 screen_write_putc(ctx, &gc, '$');
2980 }
2981}
2982
2983static void
2984window_copy_write_lines(struct window_mode_entry *wme,
2985 struct screen_write_ctx *ctx, u_int py, u_int ny)
2986{
2987 u_int yy;
2988
2989 for (yy = py; yy < py + ny; yy++)
2990 window_copy_write_line(wme, ctx, py);
2991}
2992
2993static void
2994window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y)
2995{
2996 struct window_copy_mode_data *data = wme->data;
2997 struct grid *gd = data->backing->grid;
2998 u_int new_y, start, end;
2999
3000 new_y = data->cy;
3001 if (old_y <= new_y) {
3002 start = old_y;
3003 end = new_y;
3004 } else {
3005 start = new_y;
3006 end = old_y;
3007 }
3008
3009 /*
3010 * In word selection mode the first word on the line below the cursor
3011 * might be selected, so add this line to the redraw area.
3012 */
3013 if (data->selflag == SEL_WORD) {
3014 /* Last grid line in data coordinates. */
3015 if (end < gd->sy + data->oy - 1)
3016 end++;
3017 }
3018 window_copy_redraw_lines(wme, start, end - start + 1);
3019}
3020
3021static void
3022window_copy_redraw_lines(struct window_mode_entry *wme, u_int py, u_int ny)
3023{
3024 struct window_pane *wp = wme->wp;
3025 struct window_copy_mode_data *data = wme->data;
3026 struct screen_write_ctx ctx;
3027 u_int i;
3028
3029 screen_write_start(&ctx, wp, NULL);
3030 for (i = py; i < py + ny; i++)
3031 window_copy_write_line(wme, &ctx, i);
3032 screen_write_cursormove(&ctx, data->cx, data->cy, 0);
3033 screen_write_stop(&ctx);
3034}
3035
3036static void
3037window_copy_redraw_screen(struct window_mode_entry *wme)
3038{
3039 struct window_copy_mode_data *data = wme->data;
3040
3041 window_copy_redraw_lines(wme, 0, screen_size_y(&data->screen));
3042}
3043
3044static void
3045window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin,
3046 int no_reset)
3047{
3048 struct window_copy_mode_data *data = wme->data;
3049 u_int xx, yy;
3050
3051 yy = screen_hsize(data->backing) + data->cy - data->oy;
3052 switch (data->selflag) {
3053 case SEL_WORD:
3054 xx = data->cx;
3055 if (no_reset)
3056 break;
3057 begin = 0;
3058 if (data->dy > yy || (data->dy == yy && data->dx > xx)) {
3059 /* Right to left selection. */
3060 window_copy_cursor_previous_word_pos(wme, data->ws, 0,
3061 &xx, &yy);
3062 begin = 1;
3063
3064 /* Reset the end. */
3065 data->endselx = data->endselrx;
3066 data->endsely = data->endselry;
3067 } else {
3068 /* Left to right selection. */
3069 if (xx >= window_copy_find_length(wme, yy) ||
3070 !window_copy_in_set(wme, xx + 1, yy, data->ws))
3071 window_copy_cursor_next_word_end_pos(wme,
3072 data->ws, &xx, &yy);
3073
3074 /* Reset the start. */
3075 data->selx = data->selrx;
3076 data->sely = data->selry;
3077 }
3078 break;
3079 case SEL_LINE:
3080 if (no_reset) {
3081 xx = data->cx;
3082 break;
3083 }
3084 begin = 0;
3085 if (data->dy > yy) {
3086 /* Right to left selection. */
3087 xx = 0;
3088 begin = 1;
3089
3090 /* Reset the end. */
3091 data->endselx = data->endselrx;
3092 data->endsely = data->endselry;
3093 } else {
3094 /* Left to right selection. */
3095 xx = window_copy_find_length(wme, yy);
3096
3097 /* Reset the start. */
3098 data->selx = data->selrx;
3099 data->sely = data->selry;
3100 }
3101 break;
3102 case SEL_CHAR:
3103 xx = data->cx;
3104 break;
3105 }
3106 if (begin) {
3107 data->selx = xx;
3108 data->sely = yy;
3109 } else {
3110 data->endselx = xx;
3111 data->endsely = yy;
3112 }
3113}
3114
3115static void
3116window_copy_synchronize_cursor(struct window_mode_entry *wme, int no_reset)
3117{
3118 struct window_copy_mode_data *data = wme->data;
3119
3120 switch (data->cursordrag) {
3121 case CURSORDRAG_ENDSEL:
3122 window_copy_synchronize_cursor_end(wme, 0, no_reset);
3123 break;
3124 case CURSORDRAG_SEL:
3125 window_copy_synchronize_cursor_end(wme, 1, no_reset);
3126 break;
3127 case CURSORDRAG_NONE:
3128 break;
3129 }
3130}
3131
3132static void
3133