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