Coverage Report

Created: 2026-10-17 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/grid-reader.c
Line
Count
Source
1
/* $OpenBSD: grid-reader.c,v 1.12 2026/10/08 07:50:05 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.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 "tmux.h"
20
#include <string.h>
21
22
/* Initialise virtual cursor. */
23
void
24
grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy)
25
0
{
26
0
  gr->gd = gd;
27
0
  gr->cx = cx;
28
0
  gr->cy = cy;
29
0
}
30
31
/* Get cursor position from reader. */
32
void
33
grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy)
34
0
{
35
0
  *cx = gr->cx;
36
0
  *cy = gr->cy;
37
0
}
38
39
/* Get length of line containing the cursor. */
40
u_int
41
grid_reader_line_length(struct grid_reader *gr)
42
0
{
43
0
  return (grid_line_length(gr->gd, gr->cy));
44
0
}
45
46
/* Move cursor forward one position. */
47
void
48
grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all, int onemore)
49
0
{
50
0
  u_int     px;
51
0
  struct grid_cell  gc;
52
53
0
  if (all)
54
0
    px = gr->gd->sx;
55
0
  else if (onemore)
56
0
    px = grid_reader_line_length(gr);
57
0
  else
58
0
    px = grid_line_limit(gr->gd, gr->cy);
59
60
0
  if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
61
0
    grid_reader_cursor_start_of_line(gr, 0);
62
0
    grid_reader_cursor_down(gr);
63
0
  } else if (gr->cx < px) {
64
0
    gr->cx++;
65
0
    while (gr->cx < px) {
66
0
      grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
67
0
      if (~gc.flags & GRID_FLAG_PADDING)
68
0
        break;
69
0
      gr->cx++;
70
0
    }
71
0
  }
72
0
}
73
74
/* Move cursor back one position. */
75
void
76
grid_reader_cursor_left(struct grid_reader *gr, int wrap)
77
0
{
78
0
  struct grid_cell  gc;
79
80
0
  while (gr->cx > 0) {
81
0
    grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
82
0
    if (~gc.flags & GRID_FLAG_PADDING)
83
0
      break;
84
0
    gr->cx--;
85
0
  }
86
0
  if (gr->cx == 0 && gr->cy > 0 &&
87
0
      (wrap ||
88
0
       grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) {
89
0
    grid_reader_cursor_up(gr);
90
0
    grid_reader_cursor_end_of_line(gr, 0, 0);
91
0
  } else if (gr->cx > 0)
92
0
    gr->cx--;
93
0
}
94
95
/* Move cursor down one line. */
96
void
97
grid_reader_cursor_down(struct grid_reader *gr)
98
0
{
99
0
  struct grid_cell  gc;
100
101
0
  if (gr->cy < gr->gd->hsize + gr->gd->sy - 1)
102
0
    gr->cy++;
103
0
  while (gr->cx > 0) {
104
0
    grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
105
0
    if (~gc.flags & GRID_FLAG_PADDING)
106
0
      break;
107
0
    gr->cx--;
108
0
  }
109
0
}
110
111
/* Move cursor up one line. */
112
void
113
grid_reader_cursor_up(struct grid_reader *gr)
114
0
{
115
0
  struct grid_cell  gc;
116
117
0
  if (gr->cy > 0)
118
0
    gr->cy--;
119
0
  while (gr->cx > 0) {
120
0
    grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
121
0
    if (~gc.flags & GRID_FLAG_PADDING)
122
0
      break;
123
0
    gr->cx--;
124
0
  }
125
0
}
126
127
/* Move cursor to the start of the line. */
128
void
129
grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap)
130
0
{
131
0
  if (wrap) {
132
0
    while (gr->cy > 0 &&
133
0
        grid_get_line(gr->gd, gr->cy - 1)->flags &
134
0
            GRID_LINE_WRAPPED)
135
0
      gr->cy--;
136
0
  }
137
0
  gr->cx = 0;
138
0
}
139
140
/* Move cursor to the end of the line. */
141
void
142
grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all)
143
0
{
144
0
  u_int yy;
145
146
0
  if (wrap) {
147
0
    yy = gr->gd->hsize + gr->gd->sy - 1;
148
0
    while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags &
149
0
        GRID_LINE_WRAPPED)
150
0
      gr->cy++;
151
0
  }
152
0
  if (all)
153
0
    gr->cx = gr->gd->sx;
154
0
  else
155
0
    gr->cx = grid_reader_line_length(gr);
156
0
}
157
158
/* Handle line wrapping while moving the cursor. */
159
static int
160
grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy)
161
0
{
162
  /*
163
   * Make sure the cursor lies within the grid reader's bounding area,
164
   * wrapping to the next line as necessary. Return zero if the cursor
165
   * would wrap past the bottom of the grid.
166
   */
167
0
  while (gr->cx > *xx) {
168
0
    if (gr->cy == *yy)
169
0
      return (0);
170
0
    grid_reader_cursor_start_of_line(gr, 0);
171
0
    grid_reader_cursor_down(gr);
172
173
0
    if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
174
0
      *xx = gr->gd->sx - 1;
175
0
    else
176
0
      *xx = grid_reader_line_length(gr);
177
0
  }
178
0
  return (1);
179
0
}
180
181
/* Check if character under cursor is in set. */
182
int
183
grid_reader_in_set(struct grid_reader *gr, const char *set)
184
0
{
185
0
  return (grid_in_set(gr->gd, gr->cx, gr->cy, set));
186
0
}
187
188
/* Move cursor to the start of the next word. */
189
void
190
grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
191
0
{
192
0
  u_int xx, yy, width;
193
194
  /* Do not break up wrapped words. */
195
0
  if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
196
0
    xx = gr->gd->sx - 1;
197
0
  else
198
0
    xx = grid_reader_line_length(gr);
199
0
  yy = gr->gd->hsize + gr->gd->sy - 1;
200
201
  /*
202
   * When navigating via spaces (for example with next-space) separators
203
   * should be empty.
204
   *
205
   * If we started on a separator that is not whitespace, skip over
206
   * subsequent separators that are not whitespace. Otherwise, if we
207
   * started on a non-whitespace character, skip over subsequent
208
   * characters that are neither whitespace nor separators. Then, skip
209
   * over whitespace (if any) until the next non-whitespace character.
210
   */
211
0
  if (!grid_reader_handle_wrap(gr, &xx, &yy))
212
0
    return;
213
0
  if (!grid_reader_in_set(gr, WHITESPACE)) {
214
0
    if (grid_reader_in_set(gr, separators)) {
215
0
      do
216
0
        gr->cx++;
217
0
      while (grid_reader_handle_wrap(gr, &xx, &yy) &&
218
0
          grid_reader_in_set(gr, separators) &&
219
0
          !grid_reader_in_set(gr, WHITESPACE));
220
0
    } else {
221
0
      do
222
0
        gr->cx++;
223
0
      while (grid_reader_handle_wrap(gr, &xx, &yy) &&
224
0
          !(grid_reader_in_set(gr, separators) ||
225
0
          grid_reader_in_set(gr, WHITESPACE)));
226
0
    }
227
0
  }
228
0
  while (grid_reader_handle_wrap(gr, &xx, &yy) &&
229
0
      (width = grid_reader_in_set(gr, WHITESPACE)))
230
0
    gr->cx += width;
231
0
}
232
233
/* Move cursor to the end of the next word. */
234
void
235
grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators)
236
0
{
237
0
  u_int xx, yy;
238
239
  /* Do not break up wrapped words. */
240
0
  if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
241
0
    xx = gr->gd->sx - 1;
242
0
  else
243
0
    xx = grid_reader_line_length(gr);
244
0
  yy = gr->gd->hsize + gr->gd->sy - 1;
245
246
  /*
247
   * When navigating via spaces (for example with next-space), separators
248
   * should be empty in both modes.
249
   *
250
   * If we started on a whitespace, move until reaching the first
251
   * non-whitespace character. If that character is a separator, treat
252
   * subsequent separators as a word, and continue moving until the first
253
   * non-separator. Otherwise, continue moving until the first separator
254
   * or whitespace.
255
   */
256
257
0
  while (grid_reader_handle_wrap(gr, &xx, &yy)) {
258
0
    if (grid_reader_in_set(gr, WHITESPACE))
259
0
      gr->cx++;
260
0
    else if (grid_reader_in_set(gr, separators)) {
261
0
      do
262
0
        gr->cx++;
263
0
      while (grid_reader_handle_wrap(gr, &xx, &yy) &&
264
0
          grid_reader_in_set(gr, separators) &&
265
0
          !grid_reader_in_set(gr, WHITESPACE));
266
0
      return;
267
0
    } else {
268
0
      do
269
0
        gr->cx++;
270
0
      while (grid_reader_handle_wrap(gr, &xx, &yy) &&
271
0
          !(grid_reader_in_set(gr, WHITESPACE) ||
272
0
          grid_reader_in_set(gr, separators)));
273
0
      return;
274
0
    }
275
0
  }
276
0
}
277
278
/* Move to the previous place where a word begins. */
279
void
280
grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
281
    int already, int stop_at_eol)
282
0
{
283
0
  int oldx, oldy, at_eol, word_is_letters;
284
285
  /* Move back to the previous word character. */
286
0
  if (already || grid_reader_in_set(gr, WHITESPACE)) {
287
0
    for (;;) {
288
0
      if (gr->cx > 0) {
289
0
        gr->cx--;
290
0
        if (!grid_reader_in_set(gr, WHITESPACE)) {
291
0
          word_is_letters =
292
0
              !grid_reader_in_set(gr, separators);
293
0
          break;
294
0
        }
295
0
      } else {
296
0
        if (gr->cy == 0)
297
0
          return;
298
0
        grid_reader_cursor_up(gr);
299
0
        grid_reader_cursor_end_of_line(gr, 0, 0);
300
301
        /* Stop if separator at EOL. */
302
0
        if (stop_at_eol && gr->cx > 0) {
303
0
          oldx = gr->cx;
304
0
          gr->cx--;
305
0
          at_eol = grid_reader_in_set(gr,
306
0
              WHITESPACE);
307
0
          gr->cx = oldx;
308
0
          if (at_eol) {
309
0
            word_is_letters = 0;
310
0
            break;
311
0
          }
312
0
        }
313
0
      }
314
0
    }
315
0
  } else
316
0
    word_is_letters = !grid_reader_in_set(gr, separators);
317
318
  /* Move back to the beginning of this word. */
319
0
  do {
320
0
    oldx = gr->cx;
321
0
    oldy = gr->cy;
322
0
    if (gr->cx == 0) {
323
0
      if (gr->cy == 0 ||
324
0
          (~grid_get_line(gr->gd, gr->cy - 1)->flags &
325
0
          GRID_LINE_WRAPPED))
326
0
        break;
327
0
      grid_reader_cursor_up(gr);
328
0
      grid_reader_cursor_end_of_line(gr, 0, 1);
329
0
    }
330
0
    if (gr->cx > 0)
331
0
      gr->cx--;
332
0
  } while (!grid_reader_in_set(gr, WHITESPACE) &&
333
0
      word_is_letters != grid_reader_in_set(gr, separators));
334
0
  gr->cx = oldx;
335
0
  gr->cy = oldy;
336
0
}
337
338
/* Compare grid cell to UTF-8 data. Return 1 if equal, 0 if not. */
339
static int
340
grid_reader_cell_equals_data(const struct grid_cell *gc,
341
    const struct utf8_data *ud)
342
0
{
343
0
  if (gc->flags & GRID_FLAG_PADDING)
344
0
    return (0);
345
0
  if (gc->flags & GRID_FLAG_TAB && ud->size == 1 && *ud->data == '\t')
346
0
    return (1);
347
0
  if (gc->data.size != ud->size)
348
0
    return (0);
349
0
  return (memcmp(gc->data.data, ud->data, gc->data.size) == 0);
350
0
}
351
352
/* Jump forward to character. */
353
int
354
grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
355
0
{
356
0
  struct grid_cell  gc;
357
0
  u_int     px, py, xx, yy;
358
359
0
  px = gr->cx;
360
0
  yy = gr->gd->hsize + gr->gd->sy - 1;
361
362
0
  for (py = gr->cy; py <= yy; py++) {
363
0
    xx = grid_line_length(gr->gd, py);
364
0
    while (px < xx) {
365
0
      grid_get_cell(gr->gd, px, py, &gc);
366
0
      if (grid_reader_cell_equals_data(&gc, jc)) {
367
0
        gr->cx = px;
368
0
        gr->cy = py;
369
0
        return (1);
370
0
      }
371
0
      px++;
372
0
    }
373
374
0
    if (py == yy ||
375
0
        !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED))
376
0
      return (0);
377
0
    px = 0;
378
0
  }
379
0
  return (0);
380
0
}
381
382
/* Jump back to character. */
383
int
384
grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
385
0
{
386
0
  struct grid_cell  gc;
387
0
  u_int     px, py, xx;
388
389
0
  xx = gr->cx + 1;
390
391
0
  for (py = gr->cy + 1; py > 0; py--) {
392
0
    for (px = xx; px > 0; px--) {
393
0
      grid_get_cell(gr->gd, px - 1, py - 1, &gc);
394
0
      if (grid_reader_cell_equals_data(&gc, jc)) {
395
0
        gr->cx = px - 1;
396
0
        gr->cy = py - 1;
397
0
        return (1);
398
0
      }
399
0
    }
400
401
0
    if (py == 1 ||
402
0
        !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED))
403
0
      return (0);
404
0
    xx = grid_line_length(gr->gd, py - 2);
405
0
  }
406
0
  return (0);
407
0
}
408
409
/* Jump back to the first non-blank character of the line. */
410
void
411
grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
412
0
{
413
0
  struct grid_cell  gc;
414
0
  u_int     px, py, xx, yy, oldx, oldy;
415
416
0
  yy = gr->gd->hsize + gr->gd->sy - 1;
417
0
  oldx = gr->cx;
418
0
  oldy = gr->cy;
419
0
  grid_reader_cursor_start_of_line(gr, 1);
420
421
0
  for (py = gr->cy; py <= yy; py++) {
422
0
    xx = grid_line_length(gr->gd, py);
423
0
    for (px = 0; px < xx; px++) {
424
0
      grid_get_cell(gr->gd, px, py, &gc);
425
0
      if ((gc.data.size != 1 || *gc.data.data != ' ') &&
426
0
          ~gc.flags & GRID_FLAG_TAB &&
427
0
          ~gc.flags & GRID_FLAG_PADDING) {
428
0
        gr->cx = px;
429
0
        gr->cy = py;
430
0
        return;
431
0
      }
432
0
    }
433
0
    if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)
434
0
      break;
435
0
  }
436
0
  gr->cx = oldx;
437
0
  gr->cy = oldy;
438
0
}
439
440
/* Get the end of the last used line. */
441
static void
442
grid_reader_output_end(struct grid *gd, u_int *x, u_int *y)
443
0
{
444
0
  u_int last = gd->hsize + gd->sy - 1;
445
446
0
  while (last > 0 && grid_get_line(gd, last)->cellused == 0)
447
0
    last--;
448
0
  *x = grid_get_line(gd, last)->cellused;
449
0
  *y = last;
450
0
}
451
452
/* Find the most recent complete output at or before the cursor. */
453
static int
454
grid_reader_previous_output_range(struct grid_reader *gr, u_int *sx,
455
    u_int *sy, u_int *ex, u_int *ey)
456
0
{
457
0
  struct grid   *gd = gr->gd;
458
0
  struct grid_line  *gl;
459
0
  struct osc133_data  *od;
460
0
  u_int      cursor_x = gr->cx, cursor_y = gr->cy;
461
0
  u_int      start_x, start_y, y, total;
462
0
  int      found = 0, have_prompt = 0;
463
0
  int      pending = 0;
464
0
  int      has_start, has_end, end_first;
465
0
  int      cleared;
466
467
0
  total = gd->hsize + gd->sy;
468
469
  /*
470
   * Scan from the top because markers arrive in their natural order, C
471
   * then D. A backward scan would meet D first and need extra state to
472
   * find its C. The last complete output found is the one wanted.
473
   */
474
0
  for (y = 0; y < total && y <= cursor_y; y++) {
475
0
    gl = grid_get_line(gd, y);
476
0
    od = &gl->osc133_data;
477
478
    /* On the cursor's line, ignore markers after the cursor. */
479
0
    has_start = has_end = end_first = 0;
480
0
    if (gl->flags & GRID_LINE_START_OUTPUT) {
481
0
      if (y != cursor_y || od->out_start_col <= cursor_x)
482
0
        has_start = 1;
483
0
    }
484
0
    if (gl->flags & GRID_LINE_END_OUTPUT) {
485
0
      if (y != cursor_y || od->out_end_col <= cursor_x)
486
0
        has_end = 1;
487
0
    }
488
489
    /* An end before the start ends the previous output. */
490
0
    if (has_start && has_end && od->out_end_col < od->out_start_col)
491
0
      end_first = 1;
492
493
    /* A C marker starts an output that is pending until its D. */
494
0
    if (has_start && !end_first) {
495
0
      start_x = od->out_start_col;
496
0
      start_y = y;
497
0
      pending = 1;
498
0
    }
499
500
    /* The output may have cleared its C marker from the screen. */
501
0
    if (!pending && !have_prompt &&
502
0
        (gl->flags & GRID_LINE_END_OUTPUT)) {
503
0
      cleared = 1;
504
0
      if ((gl->flags & GRID_LINE_START_PROMPT) &&
505
0
          od->out_end_col > od->prompt_col)
506
0
        cleared = 0;
507
0
      if (cleared) {
508
0
        start_x = start_y = 0;
509
0
        pending = 1;
510
0
      }
511
0
    }
512
513
    /* A D marker completes the pending output. */
514
0
    if (pending && has_end) {
515
0
      *sx = start_x;
516
0
      *sy = start_y;
517
0
      *ex = od->out_end_col;
518
0
      *ey = y;
519
0
      found = 1;
520
0
      pending = 0;
521
0
    }
522
0
    if (has_start && end_first) {
523
0
      start_x = od->out_start_col;
524
0
      start_y = y;
525
0
      pending = 1;
526
0
    }
527
528
    /* A prompt drops an unfinished output. */
529
0
    if (gl->flags & GRID_LINE_START_PROMPT) {
530
0
      if (!has_start || od->out_start_col < od->prompt_col)
531
0
        pending = 0;
532
0
      have_prompt = 1;
533
0
    }
534
0
  }
535
0
  return (found);
536
0
}
537
538
/*
539
 * Find the output range for the command at the cursor, from its C marker to
540
 * its D marker. If the prompt has no output yet, use the previous output.
541
 * Returns 0 if there is no usable range.
542
 */
543
int
544
grid_reader_output_range(struct grid_reader *gr, u_int *sx, u_int *sy,
545
    u_int *ex, u_int *ey)
546
0
{
547
0
  struct grid   *gd = gr->gd;
548
0
  struct grid_line  *gl;
549
0
  struct osc133_data  *od;
550
0
  u_int      cursor_x = gr->cx, cursor_y = gr->cy;
551
0
  u_int      prompt_x = 0, prompt_y = UINT_MAX;
552
0
  u_int      y, total;
553
0
  int      found_start = 0, found_end = 0;
554
0
  int      next_prompt, in_range, end_ok;
555
0
  int      found;
556
557
  /* Find the last prompt at or before the cursor. */
558
0
  total = gd->hsize + gd->sy;
559
0
  for (y = 0; y < total; y++) {
560
0
    gl = grid_get_line(gd, y);
561
0
    od = &gl->osc133_data;
562
0
    if (~gl->flags & GRID_LINE_START_PROMPT)
563
0
      continue;
564
0
    if (y > cursor_y)
565
0
      break;
566
0
    if (y == cursor_y && od->prompt_col > cursor_x)
567
0
      break;
568
0
    prompt_y = y;
569
0
    prompt_x = od->prompt_col;
570
0
  }
571
572
  /* With no prompt, its A marker may have left history, so start at 0. */
573
0
  if (prompt_y == UINT_MAX)
574
0
    y = 0;
575
0
  else
576
0
    y = prompt_y;
577
578
  /* Walk down from the prompt to its C and D, up to the next prompt. */
579
0
  for (; y < total; y++) {
580
0
    gl = grid_get_line(gd, y);
581
0
    od = &gl->osc133_data;
582
0
    next_prompt = 0;
583
0
    if (y != prompt_y && (gl->flags & GRID_LINE_START_PROMPT))
584
0
      next_prompt = 1;
585
586
    /* Output before the next prompt on its line is ours. */
587
0
    if (y == prompt_y)
588
0
      in_range = (od->out_start_col >= prompt_x);
589
0
    else if (next_prompt)
590
0
      in_range = (od->out_start_col < od->prompt_col);
591
0
    else
592
0
      in_range = 1;
593
0
    if (gl->flags & GRID_LINE_START_OUTPUT && in_range) {
594
0
      *sx = od->out_start_col;
595
0
      *sy = y;
596
0
      found_start = 1;
597
0
    }
598
599
    /* Both A and C may have left history while D remains. */
600
0
    if (!found_start && prompt_y == UINT_MAX &&
601
0
        (gl->flags & GRID_LINE_END_OUTPUT)) {
602
0
      if (!next_prompt || od->out_end_col <= od->prompt_col) {
603
0
        *sx = *sy = 0;
604
0
        found_start = 1;
605
0
      }
606
0
    }
607
608
    /* An output may end on the same line or the next prompt's. */
609
0
    if (found_start && (gl->flags & GRID_LINE_END_OUTPUT)) {
610
0
      end_ok = 1;
611
0
      if (y == prompt_y && od->out_end_col < prompt_x)
612
0
        end_ok = 0;
613
0
      if (y == *sy && od->out_end_col < *sx)
614
0
        end_ok = 0;
615
0
      if (end_ok) {
616
0
        *ex = od->out_end_col;
617
0
        *ey = y;
618
0
        found_end = 1;
619
0
        break;
620
0
      }
621
0
    }
622
0
    if (next_prompt)
623
0
      break;
624
0
  }
625
626
  /* The cursor is on a prompt with no output yet. */
627
0
  if (!found_start) {
628
0
    found = grid_reader_previous_output_range(gr, sx, sy, ex, ey);
629
0
    return (found);
630
0
  }
631
632
  /*
633
   * Without a D marker the command is still running, so its output runs
634
   * to the last used line. If the next prompt came first, it has no
635
   * usable end.
636
   */
637
0
  if (!found_end) {
638
0
    if (y != total)
639
0
      return (0);
640
0
    grid_reader_output_end(gd, ex, ey);
641
0
  }
642
0
  return (1);
643
0
}