Coverage Report

Created: 2026-04-12 06:58

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