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