Bug Summary

File:src/usr.bin/tmux/window-copy.c
Warning:line 3621, column 7
Array access (via field 'searchmark') results in a null pointer dereference

Annotated Source Code

Press '?' to see keyboard shortcuts

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