Coverage Report

Created: 2025-08-29 06:28

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