Coverage Report

Created: 2026-07-16 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/grid.c
Line
Count
Source
1
/* $OpenBSD: grid.c,v 1.153 2026/07/02 08:51:05 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2008 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
#ifdef __APPLE__
22
#include <assert.h>
23
#endif
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "tmux.h"
28
29
/*
30
 * Grid data. This is the basic data structure that represents what is shown on
31
 * screen.
32
 *
33
 * A grid is a grid of cells (struct grid_cell). Lines are not allocated until
34
 * cells in that line are written to. The grid is split into history and
35
 * viewable data with the history starting at row (line) 0 and extending to
36
 * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All
37
 * functions in this file work on absolute coordinates, grid-view.c has
38
 * functions which work on the screen data.
39
 */
40
41
/* Default grid cell data. */
42
const struct grid_cell grid_default_cell = {
43
  { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
44
};
45
46
/*
47
 * Padding grid cell data. Padding cells are the only zero width cell that
48
 * appears in the grid - because of this, they are always extended cells.
49
 */
50
static const struct grid_cell grid_padding_cell = {
51
  { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
52
};
53
54
/* Cleared grid cell data. */
55
static const struct grid_cell grid_cleared_cell = {
56
  { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
57
};
58
static const struct grid_cell_entry grid_cleared_entry = {
59
  { .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
60
};
61
62
#ifdef __APPLE__
63
void
64
grid_check_is_clear(struct grid *gd)
65
{
66
  struct grid_line  *gl;
67
  u_int      yy, ny;
68
69
  assert(gd != NULL);
70
71
  if (gd->sy == 0) {
72
    assert(gd->linedata == NULL);
73
    return;
74
  }
75
76
  assert(gd->linedata != NULL);
77
78
  ny = gd->hsize + gd->sy;
79
  for (yy = 0; yy < ny; yy++) {
80
    gl = &gd->linedata[yy];
81
82
    assert(gl->celldata == NULL);
83
    assert(gl->cellused == 0);
84
    assert(gl->cellsize == 0);
85
    assert(gl->extddata == NULL);
86
    assert(gl->extdsize == 0);
87
    assert(gl->flags == 0);
88
    assert(gl->time == 0);
89
  }
90
}
91
#else
92
void
93
grid_check_is_clear(__unused struct grid *gd)
94
46.8k
{
95
46.8k
}
96
#endif
97
98
/* Store cell in entry. */
99
static void
100
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
101
    u_char c)
102
2.35M
{
103
2.35M
  gce->flags = (gc->flags & ~GRID_FLAG_CLEARED);
104
105
2.35M
  gce->data.fg = gc->fg & 0xff;
106
2.35M
  if (gc->fg & COLOUR_FLAG_256)
107
798
    gce->flags |= GRID_FLAG_FG256;
108
109
2.35M
  gce->data.bg = gc->bg & 0xff;
110
2.35M
  if (gc->bg & COLOUR_FLAG_256)
111
622
    gce->flags |= GRID_FLAG_BG256;
112
113
2.35M
  gce->data.attr = gc->attr;
114
2.35M
  gce->data.data = c;
115
2.35M
}
116
117
/* Check if a cell should be an extended cell. */
118
static int
119
grid_need_extended_cell(const struct grid_cell_entry *gce,
120
    const struct grid_cell *gc)
121
2.39M
{
122
2.39M
  if (gce->flags & GRID_FLAG_EXTENDED)
123
8.01k
    return (1);
124
2.38M
  if (gc->attr > 0xff)
125
762
    return (1);
126
2.38M
  if (gc->data.size > 1 || gc->data.width > 1)
127
7.52k
    return (1);
128
2.37M
  if ((gc->fg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)) ||
129
2.37M
      (gc->bg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)))
130
1.25k
    return (1);
131
2.37M
  if (gc->us != 8) /* only supports 256 or RGB */
132
3.99k
    return (1);
133
2.37M
  if (gc->link != 0)
134
12.7k
    return (1);
135
2.36M
  if (gc->flags & GRID_FLAG_TAB)
136
2.26k
    return (1);
137
2.35M
  return (0);
138
2.36M
}
139
140
/* Get an extended cell. */
141
static void
142
grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
143
    int flags)
144
139k
{
145
139k
  u_int at = gl->extdsize + 1;
146
147
139k
  gl->extddata = xreallocarray(gl->extddata, at, sizeof *gl->extddata);
148
139k
  gl->extdsize = at;
149
150
139k
  gce->offset = at - 1;
151
139k
  gce->flags = (flags | GRID_FLAG_EXTENDED);
152
139k
}
153
154
/* Set cell as extended. */
155
static struct grid_extd_entry *
156
grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
157
    const struct grid_cell *gc)
158
150k
{
159
150k
  struct grid_extd_entry  *gee;
160
150k
  int      flags = (gc->flags & ~GRID_FLAG_CLEARED);
161
150k
  utf8_char    uc;
162
163
150k
  if (~gce->flags & GRID_FLAG_EXTENDED)
164
28.5k
    grid_get_extended_cell(gl, gce, flags);
165
121k
  else if (gce->offset >= gl->extdsize)
166
0
    fatalx("offset too big");
167
150k
  gl->flags |= GRID_LINE_EXTENDED;
168
150k
  if (gc->link != 0)
169
13.1k
    gl->flags |= GRID_LINE_HYPERLINK;
170
171
150k
  if (gc->flags & GRID_FLAG_TAB)
172
6.00k
    uc = gc->data.width;
173
144k
  else
174
144k
    utf8_from_data(&gc->data, &uc);
175
176
150k
  gee = &gl->extddata[gce->offset];
177
150k
  gee->data = uc;
178
150k
  gee->attr = gc->attr;
179
150k
  gee->flags = flags;
180
150k
  gee->fg = gc->fg;
181
150k
  gee->bg = gc->bg;
182
150k
  gee->us = gc->us;
183
150k
  gee->link = gc->link;
184
150k
  return (gee);
185
150k
}
186
187
/* Free up unused extended cells. */
188
static void
189
grid_compact_line(struct grid_line *gl)
190
0
{
191
0
  int      new_extdsize = 0;
192
0
  struct grid_extd_entry  *new_extddata;
193
0
  struct grid_cell_entry  *gce;
194
0
  struct grid_extd_entry  *gee;
195
0
  u_int      px, idx;
196
197
0
  if (gl->extdsize == 0)
198
0
    return;
199
200
0
  for (px = 0; px < gl->cellsize; px++) {
201
0
    gce = &gl->celldata[px];
202
0
    if (gce->flags & GRID_FLAG_EXTENDED)
203
0
      new_extdsize++;
204
0
  }
205
206
0
  if (new_extdsize == 0) {
207
0
    free(gl->extddata);
208
0
    gl->extddata = NULL;
209
0
    gl->extdsize = 0;
210
0
    return;
211
0
  }
212
0
  new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata);
213
214
0
  idx = 0;
215
0
  for (px = 0; px < gl->cellsize; px++) {
216
0
    gce = &gl->celldata[px];
217
0
    if (gce->flags & GRID_FLAG_EXTENDED) {
218
0
      gee = &gl->extddata[gce->offset];
219
0
      memcpy(&new_extddata[idx], gee, sizeof *gee);
220
0
      gce->offset = idx++;
221
0
    }
222
0
  }
223
224
0
  free(gl->extddata);
225
0
  gl->extddata = new_extddata;
226
0
  gl->extdsize = new_extdsize;
227
0
}
228
229
/* Get line data. */
230
struct grid_line *
231
grid_get_line(struct grid *gd, u_int line)
232
47.5k
{
233
47.5k
  return (&gd->linedata[line]);
234
47.5k
}
235
236
/* Adjust number of lines. */
237
void
238
grid_adjust_lines(struct grid *gd, u_int lines)
239
0
{
240
0
  gd->linedata = xreallocarray(gd->linedata, lines, sizeof *gd->linedata);
241
0
}
242
243
/* Copy default into a cell. */
244
static void
245
grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg, int moved)
246
977k
{
247
977k
  struct grid_line  *gl = &gd->linedata[py];
248
977k
  struct grid_cell_entry  *gce = &gl->celldata[px];
249
977k
  struct grid_extd_entry  *gee;
250
977k
  u_int      old_offset = gce->offset;
251
977k
  int      had_extd = (gce->flags & GRID_FLAG_EXTENDED);
252
253
977k
  memcpy(gce, &grid_cleared_entry, sizeof *gce);
254
977k
  if (!moved && had_extd && old_offset < gl->extdsize) {
255
2.56k
    gce->flags |= GRID_FLAG_EXTENDED;
256
2.56k
    gce->offset = old_offset;
257
2.56k
    gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
258
2.56k
    if (bg != 8)
259
1.85k
      gee->bg = bg;
260
975k
  } else if (bg != 8) {
261
312k
    if (bg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)) {
262
110k
      grid_get_extended_cell(gl, gce, gce->flags);
263
110k
      gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
264
110k
      gee->bg = bg;
265
201k
    } else {
266
201k
      if (bg & COLOUR_FLAG_256)
267
4.40k
        gce->flags |= GRID_FLAG_BG256;
268
201k
      gce->data.bg = bg;
269
201k
    }
270
312k
  }
271
977k
}
272
273
/* Check grid y position. */
274
static int
275
grid_check_y(struct grid *gd, const char *from, u_int py)
276
2.78M
{
277
2.78M
  if (py >= gd->hsize + gd->sy) {
278
0
    log_debug("%s: y out of range: %u", from, py);
279
0
    return (-1);
280
0
  }
281
2.78M
  return (0);
282
2.78M
}
283
284
/* Check if two styles are (visibly) the same. */
285
int
286
grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
287
160k
{
288
160k
  int flags1 = gc1->flags, flags2 = gc2->flags;
289
290
160k
  if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
291
1.23k
    return (0);
292
159k
  if (gc1->attr != gc2->attr)
293
677
    return (0);
294
158k
  if ((flags1 & ~GRID_FLAG_CLEARED) != (flags2 & ~GRID_FLAG_CLEARED))
295
417
    return (0);
296
158k
  if (gc1->link != gc2->link)
297
399
    return (0);
298
158k
  return (1);
299
158k
}
300
301
/* Compare grid cells. Return 1 if equal, 0 if not. */
302
int
303
grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
304
3.86k
{
305
3.86k
  if (!grid_cells_look_equal(gc1, gc2))
306
1.68k
    return (0);
307
2.17k
  if (gc1->data.width != gc2->data.width)
308
0
    return (0);
309
2.17k
  if (gc1->data.size != gc2->data.size)
310
941
    return (0);
311
1.23k
  return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0);
312
2.17k
}
313
314
/* Set grid cell to a tab. */
315
void
316
grid_set_tab(struct grid_cell *gc, u_int width)
317
8.96k
{
318
8.96k
  memset(gc->data.data, 0, sizeof gc->data.data);
319
8.96k
  gc->flags |= GRID_FLAG_TAB;
320
8.96k
  gc->flags &= ~GRID_FLAG_PADDING;
321
8.96k
  gc->data.width = gc->data.size = gc->data.have = width;
322
8.96k
  memset(gc->data.data, ' ', gc->data.size);
323
8.96k
}
324
325
/* Free one line. */
326
static void
327
grid_free_line(struct grid *gd, u_int py)
328
744k
{
329
744k
  struct grid_line  *gl = &gd->linedata[py];
330
331
#ifdef __APPLE__
332
  assert(gl->cellused <= gl->cellsize);
333
  assert(gl->extdsize == 0 || gl->extddata != NULL);
334
  assert(gl->cellsize == 0 || gl->celldata != NULL);
335
#endif
336
337
744k
  free(gl->celldata);
338
744k
  free(gl->extddata);
339
744k
  memset(gl, 0, sizeof *gl);
340
744k
}
341
342
/* Free several lines. */
343
void
344
grid_free_lines(struct grid *gd, u_int py, u_int ny)
345
24.5k
{
346
24.5k
  u_int yy;
347
348
354k
  for (yy = py; yy < py + ny; yy++)
349
329k
    grid_free_line(gd, yy);
350
24.5k
}
351
352
/* Create a new grid. */
353
struct grid *
354
grid_create(u_int sx, u_int sy, u_int hlimit)
355
23.6k
{
356
23.6k
  struct grid *gd;
357
358
23.6k
  gd = xcalloc(1, sizeof *gd);
359
23.6k
  gd->sx = sx;
360
23.6k
  gd->sy = sy;
361
362
23.6k
  if (hlimit != 0)
363
0
    gd->flags = GRID_HISTORY;
364
23.6k
  gd->hlimit = hlimit;
365
366
23.6k
  if (gd->sy != 0)
367
23.6k
    gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
368
369
#ifdef __APPLE__
370
  assert(gd->hsize == 0);
371
#endif
372
23.6k
  grid_check_is_clear(gd);
373
23.6k
  return (gd);
374
23.6k
}
375
376
/* Destroy grid. */
377
void
378
grid_destroy(struct grid *gd)
379
23.6k
{
380
23.6k
  grid_free_lines(gd, 0, gd->hsize + gd->sy);
381
23.6k
  free(gd->linedata);
382
23.6k
  free(gd);
383
23.6k
}
384
385
/* Compare grids. */
386
int
387
grid_compare(struct grid *ga, struct grid *gb)
388
0
{
389
0
  struct grid_line  *gla, *glb;
390
0
  struct grid_cell   gca, gcb;
391
0
  u_int      xx, yy;
392
393
0
  if (ga->sx != gb->sx || ga->sy != gb->sy)
394
0
    return (1);
395
396
0
  for (yy = 0; yy < ga->sy; yy++) {
397
0
    gla = &ga->linedata[yy];
398
0
    glb = &gb->linedata[yy];
399
0
    if (gla->cellsize != glb->cellsize)
400
0
      return (1);
401
0
    for (xx = 0; xx < gla->cellsize; xx++) {
402
0
      grid_get_cell(ga, xx, yy, &gca);
403
0
      grid_get_cell(gb, xx, yy, &gcb);
404
0
      if (!grid_cells_equal(&gca, &gcb))
405
0
        return (1);
406
0
    }
407
0
  }
408
409
0
  return (0);
410
0
}
411
412
/* Trim lines from the history. */
413
static void
414
grid_trim_history(struct grid *gd, u_int ny)
415
264
{
416
264
  u_int remaining;
417
418
264
  grid_free_lines(gd, 0, ny);
419
264
  remaining = gd->hsize + gd->sy - ny;
420
264
  memmove(&gd->linedata[0], &gd->linedata[ny],
421
264
      remaining * (sizeof *gd->linedata));
422
264
  memset(&gd->linedata[remaining], 0, ny * (sizeof *gd->linedata));
423
264
}
424
425
/*
426
 * Collect lines from the history if at the limit. Free the top (oldest) 10%
427
 * and shift up.
428
 */
429
void
430
grid_collect_history(struct grid *gd, int all)
431
0
{
432
0
  u_int ny;
433
434
0
  if (gd->hsize == 0 || gd->hsize < gd->hlimit)
435
0
    return;
436
437
0
  if (all)
438
0
    ny = gd->hsize - gd->hlimit;
439
0
  else
440
0
    ny = gd->hlimit / 10;
441
0
  if (ny < 1)
442
0
    ny = 1;
443
0
  if (ny > gd->hsize)
444
0
    ny = gd->hsize;
445
446
  /*
447
   * Free the lines from 0 to ny then move the remaining lines over
448
   * them.
449
   */
450
0
  grid_trim_history(gd, ny);
451
452
0
  gd->hsize -= ny;
453
0
  gd->scroll_collected += ny;
454
0
  if (gd->hscrolled > gd->hsize)
455
0
    gd->hscrolled = gd->hsize;
456
0
}
457
458
/* Remove lines from the bottom of the history. */
459
void
460
grid_remove_history(struct grid *gd, u_int ny)
461
0
{
462
0
  u_int yy, start;
463
464
0
  if (ny > gd->hsize)
465
0
    return;
466
0
  start = gd->hsize + gd->sy - ny;
467
0
  for (yy = 0; yy < ny; yy++)
468
0
    grid_free_line(gd, start + yy);
469
0
  memset(&gd->linedata[start], 0, ny * sizeof *gd->linedata);
470
0
  gd->hsize -= ny;
471
0
}
472
473
/*
474
 * Scroll the entire visible screen, moving one line into the history. Just
475
 * allocate a new line at the bottom and move the history size indicator.
476
 */
477
void
478
grid_scroll_history(struct grid *gd, u_int bg)
479
0
{
480
0
  u_int yy;
481
482
0
  yy = gd->hsize + gd->sy;
483
0
  gd->linedata = xreallocarray(gd->linedata, yy + 1,
484
0
      sizeof *gd->linedata);
485
0
  grid_empty_line(gd, yy, bg);
486
487
0
  gd->hscrolled++;
488
0
  grid_compact_line(&gd->linedata[gd->hsize]);
489
0
  gd->linedata[gd->hsize].time = current_time;
490
0
  gd->hsize++;
491
0
  gd->scroll_added++;
492
0
}
493
494
/* Clear the history. */
495
void
496
grid_clear_history(struct grid *gd)
497
264
{
498
264
  grid_trim_history(gd, gd->hsize);
499
500
264
  gd->hscrolled = 0;
501
264
  gd->hsize = 0;
502
264
  gd->scroll_generation++;
503
504
264
  gd->linedata = xreallocarray(gd->linedata, gd->sy,
505
264
      sizeof *gd->linedata);
506
264
}
507
508
/* Scroll a region up, moving the top line into the history. */
509
void
510
grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
511
0
{
512
0
  struct grid_line  *gl_history, *gl_upper;
513
0
  u_int      yy;
514
515
  /* Create a space for a new line. */
516
0
  yy = gd->hsize + gd->sy;
517
0
  gd->linedata = xreallocarray(gd->linedata, yy + 1,
518
0
      sizeof *gd->linedata);
519
520
  /* Move the entire screen down to free a space for this line. */
521
0
  gl_history = &gd->linedata[gd->hsize];
522
0
  memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);
523
524
  /* Adjust the region and find its start and end. */
525
0
  upper++;
526
0
  gl_upper = &gd->linedata[upper];
527
0
  lower++;
528
529
  /* Move the line into the history. */
530
0
  memcpy(gl_history, gl_upper, sizeof *gl_history);
531
0
  gl_history->time = current_time;
532
533
  /* Then move the region up and clear the bottom line. */
534
0
  memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
535
0
  grid_empty_line(gd, lower, bg);
536
537
  /* Move the history offset down over the line. */
538
0
  gd->hscrolled++;
539
0
  gd->hsize++;
540
0
  gd->scroll_added++;
541
0
}
542
543
/* Expand line to fit to cell. */
544
static void
545
grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
546
2.43M
{
547
2.43M
  struct grid_line  *gl;
548
2.43M
  u_int      xx;
549
550
2.43M
  gl = &gd->linedata[py];
551
2.43M
  if (sx <= gl->cellsize)
552
2.41M
    return;
553
554
21.5k
  if (sx < gd->sx / 4)
555
7.18k
    sx = gd->sx / 4;
556
14.3k
  else if (sx < gd->sx / 2)
557
3.96k
    sx = gd->sx / 2;
558
10.3k
  else if (gd->sx > sx)
559
4.26k
    sx = gd->sx;
560
561
21.5k
  gl->celldata = xreallocarray(gl->celldata, sx,
562
21.5k
      sizeof *gl->celldata);
563
21.5k
  if (gl->cellsize < sx) {
564
21.5k
    memset(gl->celldata + gl->cellsize, 0,
565
21.5k
        (sx - gl->cellsize) * sizeof *gl->celldata);
566
21.5k
  }
567
931k
  for (xx = gl->cellsize; xx < sx; xx++)
568
910k
    grid_clear_cell(gd, xx, py, bg, 0);
569
21.5k
  gl->cellsize = sx;
570
21.5k
}
571
572
/* Empty a line and set background colour if needed. */
573
void
574
grid_empty_line(struct grid *gd, u_int py, u_int bg)
575
414k
{
576
414k
  memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
577
414k
  if (!COLOUR_DEFAULT(bg))
578
3.56k
    grid_expand_line(gd, py, gd->sx, bg);
579
414k
}
580
581
/* Peek at grid line. */
582
const struct grid_line *
583
grid_peek_line(struct grid *gd, u_int py)
584
0
{
585
0
  if (grid_check_y(gd, __func__, py) != 0)
586
0
    return (NULL);
587
0
  return (&gd->linedata[py]);
588
0
}
589
590
/* Get cell from line. */
591
static void
592
grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
593
127k
{
594
127k
  struct grid_cell_entry  *gce = &gl->celldata[px];
595
127k
  struct grid_extd_entry  *gee;
596
597
127k
  if (gce->flags & GRID_FLAG_EXTENDED) {
598
12.1k
    if (gce->offset >= gl->extdsize)
599
0
      memcpy(gc, &grid_default_cell, sizeof *gc);
600
12.1k
    else {
601
12.1k
      gee = &gl->extddata[gce->offset];
602
12.1k
      gc->flags = gee->flags;
603
12.1k
      gc->attr = gee->attr;
604
12.1k
      gc->fg = gee->fg;
605
12.1k
      gc->bg = gee->bg;
606
12.1k
      gc->us = gee->us;
607
12.1k
      gc->link = gee->link;
608
609
12.1k
      if (gc->flags & GRID_FLAG_TAB)
610
5.03k
        grid_set_tab(gc, gee->data);
611
7.07k
      else
612
7.07k
        utf8_to_data(gee->data, &gc->data);
613
12.1k
    }
614
12.1k
    return;
615
12.1k
  }
616
617
114k
  gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
618
114k
  gc->attr = gce->data.attr;
619
114k
  gc->fg = gce->data.fg;
620
114k
  if (gce->flags & GRID_FLAG_FG256)
621
130
    gc->fg |= COLOUR_FLAG_256;
622
114k
  gc->bg = gce->data.bg;
623
114k
  if (gce->flags & GRID_FLAG_BG256)
624
22
    gc->bg |= COLOUR_FLAG_256;
625
114k
  gc->us = 8;
626
114k
  utf8_set(&gc->data, gce->data.data);
627
114k
  gc->link = 0;
628
114k
}
629
630
/* Get cell for reading. */
631
void
632
grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
633
232k
{
634
232k
  if (grid_check_y(gd, __func__, py) != 0 ||
635
232k
      px >= gd->linedata[py].cellsize)
636
105k
    memcpy(gc, &grid_default_cell, sizeof *gc);
637
127k
  else
638
127k
    grid_get_cell1(&gd->linedata[py], px, gc);
639
232k
}
640
641
/* Set cell at position. */
642
void
643
grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
644
2.37M
{
645
2.37M
  struct grid_line  *gl;
646
2.37M
  struct grid_cell_entry  *gce;
647
648
2.37M
  if (grid_check_y(gd, __func__, py) != 0)
649
0
    return;
650
651
2.37M
  grid_expand_line(gd, py, px + 1, 8);
652
653
2.37M
  gl = &gd->linedata[py];
654
2.37M
  if (px + 1 > gl->cellused)
655
239k
    gl->cellused = px + 1;
656
657
2.37M
  gce = &gl->celldata[px];
658
2.37M
  if (grid_need_extended_cell(gce, gc))
659
33.8k
    grid_extended_cell(gl, gce, gc);
660
2.34M
  else
661
2.34M
    grid_store_cell(gce, gc, gc->data.data[0]);
662
2.37M
}
663
664
/* Set padding at position. */
665
void
666
grid_set_padding(struct grid *gd, u_int px, u_int py)
667
22.5k
{
668
22.5k
  grid_set_cell(gd, px, py, &grid_padding_cell);
669
22.5k
}
670
671
/* Set cells at position. */
672
void
673
grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
674
    const char *s, size_t slen)
675
8.72k
{
676
8.72k
  struct grid_line  *gl;
677
8.72k
  struct grid_cell_entry  *gce;
678
8.72k
  struct grid_extd_entry  *gee;
679
8.72k
  u_int      i;
680
681
8.72k
  if (grid_check_y(gd, __func__, py) != 0)
682
0
    return;
683
684
8.72k
  grid_expand_line(gd, py, px + slen, 8);
685
686
8.72k
  gl = &gd->linedata[py];
687
8.72k
  if (px + slen > gl->cellused)
688
5.65k
    gl->cellused = px + slen;
689
690
23.9k
  for (i = 0; i < slen; i++) {
691
15.2k
    gce = &gl->celldata[px + i];
692
15.2k
    if (grid_need_extended_cell(gce, gc)) {
693
2.69k
      gee = grid_extended_cell(gl, gce, gc);
694
2.69k
      gee->data = utf8_build_one(s[i]);
695
2.69k
    } else
696
12.5k
      grid_store_cell(gce, gc, s[i]);
697
15.2k
  }
698
8.72k
}
699
700
/* Clear area. */
701
void
702
grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
703
11.3k
{
704
11.3k
  struct grid_line  *gl;
705
11.3k
  u_int      xx, yy, ox, sx;
706
707
11.3k
  if (nx == 0 || ny == 0)
708
199
    return;
709
710
11.1k
  if (px == 0 && nx == gd->sx) {
711
7.44k
    grid_clear_lines(gd, py, ny, bg);
712
7.44k
    return;
713
7.44k
  }
714
715
3.73k
  if (grid_check_y(gd, __func__, py) != 0)
716
0
    return;
717
3.73k
  if (grid_check_y(gd, __func__, py + ny - 1) != 0)
718
0
    return;
719
720
7.47k
  for (yy = py; yy < py + ny; yy++) {
721
3.73k
    gl = &gd->linedata[yy];
722
723
3.73k
    sx = gd->sx;
724
3.73k
    if (sx > gl->cellsize)
725
2.20k
      sx = gl->cellsize;
726
3.73k
    ox = nx;
727
3.73k
    if (COLOUR_DEFAULT(bg)) {
728
3.05k
      if (px > sx)
729
418
        continue;
730
2.63k
      if (px + nx > sx)
731
1.71k
        ox = sx - px;
732
2.63k
    }
733
734
3.32k
    grid_expand_line(gd, yy, px + ox, 8); /* default bg first */
735
41.0k
    for (xx = px; xx < px + ox; xx++)
736
37.7k
      grid_clear_cell(gd, xx, yy, bg, 0);
737
3.32k
  }
738
3.73k
}
739
740
/* Clear lines. This just frees and truncates the lines. */
741
void
742
grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
743
30.6k
{
744
30.6k
  u_int yy;
745
746
30.6k
  if (ny == 0)
747
0
    return;
748
749
30.6k
  if (grid_check_y(gd, __func__, py) != 0)
750
0
    return;
751
30.6k
  if (grid_check_y(gd, __func__, py + ny - 1) != 0)
752
0
    return;
753
754
426k
  for (yy = py; yy < py + ny; yy++) {
755
395k
    grid_free_line(gd, yy);
756
395k
    grid_empty_line(gd, yy, bg);
757
395k
  }
758
30.6k
  if (py != 0)
759
3.85k
    gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
760
30.6k
}
761
762
/* Move a group of lines. */
763
void
764
grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
765
19.5k
{
766
19.5k
  u_int yy;
767
768
19.5k
  if (ny == 0 || py == dy)
769
781
    return;
770
771
18.8k
  if (grid_check_y(gd, __func__, py) != 0)
772
0
    return;
773
18.8k
  if (grid_check_y(gd, __func__, py + ny - 1) != 0)
774
0
    return;
775
18.8k
  if (grid_check_y(gd, __func__, dy) != 0)
776
0
    return;
777
18.8k
  if (grid_check_y(gd, __func__, dy + ny - 1) != 0)
778
0
    return;
779
780
  /* Free any lines which are being replaced. */
781
457k
  for (yy = dy; yy < dy + ny; yy++) {
782
438k
    if (yy >= py && yy < py + ny)
783
419k
      continue;
784
19.3k
    grid_free_line(gd, yy);
785
19.3k
  }
786
18.8k
  if (dy != 0)
787
4.52k
    gd->linedata[dy - 1].flags &= ~GRID_LINE_WRAPPED;
788
789
18.8k
  memmove(&gd->linedata[dy], &gd->linedata[py],
790
18.8k
      ny * (sizeof *gd->linedata));
791
792
  /*
793
   * Wipe any lines that have been moved (without freeing them - they are
794
   * still present).
795
   */
796
457k
  for (yy = py; yy < py + ny; yy++) {
797
438k
    if (yy < dy || yy >= dy + ny)
798
19.3k
      grid_empty_line(gd, yy, bg);
799
438k
  }
800
18.8k
  if (py != 0 && (py < dy || py >= dy + ny))
801
765
    gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
802
18.8k
}
803
804
/* Move a group of cells. */
805
void
806
grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
807
    u_int bg)
808
19.2k
{
809
19.2k
  struct grid_line  *gl;
810
19.2k
  u_int      xx;
811
812
19.2k
  if (nx == 0 || px == dx)
813
301
    return;
814
815
18.9k
  if (grid_check_y(gd, __func__, py) != 0)
816
0
    return;
817
18.9k
  gl = &gd->linedata[py];
818
819
18.9k
  grid_expand_line(gd, py, px + nx, 8);
820
18.9k
  grid_expand_line(gd, py, dx + nx, 8);
821
18.9k
  memmove(&gl->celldata[dx], &gl->celldata[px],
822
18.9k
      nx * sizeof *gl->celldata);
823
18.9k
  if (dx + nx > gl->cellused)
824
1.31k
    gl->cellused = dx + nx;
825
826
  /* Wipe any cells that have been moved. */
827
887k
  for (xx = px; xx < px + nx; xx++) {
828
868k
    if (xx >= dx && xx < dx + nx)
829
838k
      continue;
830
29.9k
    grid_clear_cell(gd, xx, py, bg, 1);
831
29.9k
  }
832
18.9k
}
833
834
/* Get ANSI foreground sequence. */
835
static size_t
836
grid_string_cells_fg(const struct grid_cell *gc, int *values)
837
0
{
838
0
  size_t  n;
839
0
  u_char  r, g, b;
840
0
  int c;
841
842
0
  n = 0;
843
0
  if (gc->fg & COLOUR_FLAG_THEME) {
844
0
    c = colour_theme_terminal_colour(gc->fg & 0xff);
845
0
    if (c == 8)
846
0
      values[n++] = 39;
847
0
    else
848
0
      values[n++] = c + 30;
849
0
  } else if (gc->fg & COLOUR_FLAG_256) {
850
0
    values[n++] = 38;
851
0
    values[n++] = 5;
852
0
    values[n++] = gc->fg & 0xff;
853
0
  } else if (gc->fg & COLOUR_FLAG_RGB) {
854
0
    values[n++] = 38;
855
0
    values[n++] = 2;
856
0
    colour_split_rgb(gc->fg, &r, &g, &b);
857
0
    values[n++] = r;
858
0
    values[n++] = g;
859
0
    values[n++] = b;
860
0
  } else {
861
0
    switch (gc->fg) {
862
0
    case 0:
863
0
    case 1:
864
0
    case 2:
865
0
    case 3:
866
0
    case 4:
867
0
    case 5:
868
0
    case 6:
869
0
    case 7:
870
0
      values[n++] = gc->fg + 30;
871
0
      break;
872
0
    case 8:
873
0
      values[n++] = 39;
874
0
      break;
875
0
    case 90:
876
0
    case 91:
877
0
    case 92:
878
0
    case 93:
879
0
    case 94:
880
0
    case 95:
881
0
    case 96:
882
0
    case 97:
883
0
      values[n++] = gc->fg;
884
0
      break;
885
0
    }
886
0
  }
887
0
  return (n);
888
0
}
889
890
/* Get ANSI background sequence. */
891
static size_t
892
grid_string_cells_bg(const struct grid_cell *gc, int *values)
893
0
{
894
0
  size_t  n;
895
0
  u_char  r, g, b;
896
0
  int c;
897
898
0
  n = 0;
899
0
  if (gc->bg & COLOUR_FLAG_THEME) {
900
0
    c = colour_theme_terminal_colour(gc->bg & 0xff);
901
0
    if (c == 8)
902
0
      values[n++] = 49;
903
0
    else
904
0
      values[n++] = c + 40;
905
0
  } else if (gc->bg & COLOUR_FLAG_256) {
906
0
    values[n++] = 48;
907
0
    values[n++] = 5;
908
0
    values[n++] = gc->bg & 0xff;
909
0
  } else if (gc->bg & COLOUR_FLAG_RGB) {
910
0
    values[n++] = 48;
911
0
    values[n++] = 2;
912
0
    colour_split_rgb(gc->bg, &r, &g, &b);
913
0
    values[n++] = r;
914
0
    values[n++] = g;
915
0
    values[n++] = b;
916
0
  } else {
917
0
    switch (gc->bg) {
918
0
    case 0:
919
0
    case 1:
920
0
    case 2:
921
0
    case 3:
922
0
    case 4:
923
0
    case 5:
924
0
    case 6:
925
0
    case 7:
926
0
      values[n++] = gc->bg + 40;
927
0
      break;
928
0
    case 8:
929
0
      values[n++] = 49;
930
0
      break;
931
0
    case 90:
932
0
    case 91:
933
0
    case 92:
934
0
    case 93:
935
0
    case 94:
936
0
    case 95:
937
0
    case 96:
938
0
    case 97:
939
0
      values[n++] = gc->bg + 10;
940
0
      break;
941
0
    }
942
0
  }
943
0
  return (n);
944
0
}
945
946
/* Get underscore colour sequence. */
947
static size_t
948
grid_string_cells_us(const struct grid_cell *gc, int *values)
949
0
{
950
0
  size_t  n;
951
0
  u_char  r, g, b;
952
0
  int c;
953
954
0
  n = 0;
955
0
  if (gc->us & COLOUR_FLAG_THEME) {
956
0
    c = colour_theme_terminal_colour(gc->us & 0xff);
957
0
    if (c == 8)
958
0
      values[n++] = 59;
959
0
    else {
960
0
      values[n++] = 58;
961
0
      values[n++] = 5;
962
0
      values[n++] = c;
963
0
    }
964
0
  } else if (gc->us & COLOUR_FLAG_256) {
965
0
    values[n++] = 58;
966
0
    values[n++] = 5;
967
0
    values[n++] = gc->us & 0xff;
968
0
  } else if (gc->us & COLOUR_FLAG_RGB) {
969
0
    values[n++] = 58;
970
0
    values[n++] = 2;
971
0
    colour_split_rgb(gc->us, &r, &g, &b);
972
0
    values[n++] = r;
973
0
    values[n++] = g;
974
0
    values[n++] = b;
975
0
  }
976
0
  return (n);
977
0
}
978
979
/* Add on SGR code. */
980
static void
981
grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
982
    int *oldc, size_t nnewc, size_t noldc, int flags)
983
0
{
984
0
  u_int i;
985
0
  char  tmp[64];
986
0
  int reset = (n != 0 && s[0] == 0);
987
988
0
  if (nnewc == 0)
989
0
    return; /* no code to add */
990
0
  if (!reset &&
991
0
      nnewc == noldc &&
992
0
      memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0)
993
0
    return; /* no reset and colour unchanged */
994
0
  if (reset && (newc[0] == 49 || newc[0] == 39))
995
0
    return; /* reset and colour default */
996
997
0
  if (flags & GRID_STRING_ESCAPE_SEQUENCES)
998
0
    strlcat(buf, "\\033[", len);
999
0
  else
1000
0
    strlcat(buf, "\033[", len);
1001
0
  for (i = 0; i < nnewc; i++) {
1002
0
    if (i + 1 < nnewc)
1003
0
      xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
1004
0
    else
1005
0
      xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
1006
0
    strlcat(buf, tmp, len);
1007
0
  }
1008
0
  strlcat(buf, "m", len);
1009
0
}
1010
1011
static int
1012
grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
1013
    const char *uri, int flags)
1014
0
{
1015
0
  char  *tmp;
1016
1017
0
  if (strlen(uri) + strlen(id) + 17 >= len)
1018
0
    return (0);
1019
1020
0
  if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1021
0
    strlcat(buf, "\\033]8;", len);
1022
0
  else
1023
0
    strlcat(buf, "\033]8;", len);
1024
0
  if (*id != '\0') {
1025
0
    xasprintf(&tmp, "id=%s;", id);
1026
0
    strlcat(buf, tmp, len);
1027
0
    free(tmp);
1028
0
  } else
1029
0
    strlcat(buf, ";", len);
1030
0
  strlcat(buf, uri, len);
1031
0
  if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1032
0
    strlcat(buf, "\\033\\\\", len);
1033
0
  else
1034
0
    strlcat(buf, "\033\\", len);
1035
0
  return (1);
1036
0
}
1037
1038
/*
1039
 * Returns ANSI code to set particular attributes (colour, bold and so on)
1040
 * given a current state.
1041
 */
1042
static void
1043
grid_string_cells_code(const struct grid_cell *lastgc,
1044
    const struct grid_cell *gc, char *buf, size_t len, int flags,
1045
    struct screen *sc, int *has_link)
1046
0
{
1047
0
  int      oldc[64], newc[64], s[128];
1048
0
  size_t       noldc, nnewc, n, i;
1049
0
  u_int      attr = gc->attr, lastattr = lastgc->attr;
1050
0
  char       tmp[64];
1051
0
  const char    *uri, *id;
1052
1053
0
  static const struct {
1054
0
    u_int mask;
1055
0
    u_int code;
1056
0
  } attrs[] = {
1057
0
    { GRID_ATTR_BRIGHT, 1 },
1058
0
    { GRID_ATTR_DIM, 2 },
1059
0
    { GRID_ATTR_ITALICS, 3 },
1060
0
    { GRID_ATTR_UNDERSCORE, 4 },
1061
0
    { GRID_ATTR_BLINK, 5 },
1062
0
    { GRID_ATTR_REVERSE, 7 },
1063
0
    { GRID_ATTR_HIDDEN, 8 },
1064
0
    { GRID_ATTR_STRIKETHROUGH, 9 },
1065
0
    { GRID_ATTR_UNDERSCORE_2, 42 },
1066
0
    { GRID_ATTR_UNDERSCORE_3, 43 },
1067
0
    { GRID_ATTR_UNDERSCORE_4, 44 },
1068
0
    { GRID_ATTR_UNDERSCORE_5, 45 },
1069
0
    { GRID_ATTR_OVERLINE, 53 },
1070
0
  };
1071
0
  n = 0;
1072
1073
  /* If any attribute is removed, begin with 0. */
1074
0
  for (i = 0; i < nitems(attrs); i++) {
1075
0
    if (((~attr & attrs[i].mask) &&
1076
0
        (lastattr & attrs[i].mask)) ||
1077
0
        (lastgc->us != 8 && gc->us == 8)) {
1078
0
      s[n++] = 0;
1079
0
      lastattr &= GRID_ATTR_CHARSET;
1080
0
      break;
1081
0
    }
1082
0
  }
1083
  /* For each attribute that is newly set, add its code. */
1084
0
  for (i = 0; i < nitems(attrs); i++) {
1085
0
    if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask))
1086
0
      s[n++] = attrs[i].code;
1087
0
  }
1088
1089
  /* Write the attributes. */
1090
0
  *buf = '\0';
1091
0
  if (n > 0) {
1092
0
    if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1093
0
      strlcat(buf, "\\033[", len);
1094
0
    else
1095
0
      strlcat(buf, "\033[", len);
1096
0
    for (i = 0; i < n; i++) {
1097
0
      if (s[i] < 10)
1098
0
        xsnprintf(tmp, sizeof tmp, "%d", s[i]);
1099
0
      else {
1100
0
        xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10,
1101
0
            s[i] % 10);
1102
0
      }
1103
0
      strlcat(buf, tmp, len);
1104
0
      if (i + 1 < n)
1105
0
        strlcat(buf, ";", len);
1106
0
    }
1107
0
    strlcat(buf, "m", len);
1108
0
  }
1109
1110
  /* If the foreground colour changed, write its parameters. */
1111
0
  nnewc = grid_string_cells_fg(gc, newc);
1112
0
  noldc = grid_string_cells_fg(lastgc, oldc);
1113
0
  grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
1114
0
      flags);
1115
1116
  /* If the background colour changed, append its parameters. */
1117
0
  nnewc = grid_string_cells_bg(gc, newc);
1118
0
  noldc = grid_string_cells_bg(lastgc, oldc);
1119
0
  grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
1120
0
      flags);
1121
1122
  /* If the underscore colour changed, append its parameters. */
1123
0
  nnewc = grid_string_cells_us(gc, newc);
1124
0
  noldc = grid_string_cells_us(lastgc, oldc);
1125
0
  grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
1126
0
      flags);
1127
1128
  /* Append shift in/shift out if needed. */
1129
0
  if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
1130
0
    if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1131
0
      strlcat(buf, "\\016", len); /* SO */
1132
0
    else
1133
0
      strlcat(buf, "\016", len);  /* SO */
1134
0
  }
1135
0
  if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
1136
0
    if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1137
0
      strlcat(buf, "\\017", len); /* SI */
1138
0
    else
1139
0
      strlcat(buf, "\017", len);  /* SI */
1140
0
  }
1141
1142
  /* Add hyperlink if changed. */
1143
0
  if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
1144
0
    if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
1145
0
      *has_link = grid_string_cells_add_hyperlink(buf, len,
1146
0
          id, uri, flags);
1147
0
    } else if (*has_link) {
1148
0
      grid_string_cells_add_hyperlink(buf, len, "", "",
1149
0
          flags);
1150
0
      *has_link = 0;
1151
0
    }
1152
0
  }
1153
0
}
1154
1155
/* Convert cells into a string. */
1156
char *
1157
grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
1158
    struct grid_cell **lastgc, int flags, struct screen *s)
1159
0
{
1160
0
  struct grid_cell   gc;
1161
0
  static struct grid_cell  lastgc1;
1162
0
  const char    *data;
1163
0
  char      *buf, code[8192];
1164
0
  size_t       len, off, size, codelen;
1165
0
  u_int      xx, end;
1166
0
  int      has_link = 0;
1167
0
  const struct grid_line  *gl;
1168
1169
0
  if (lastgc != NULL && *lastgc == NULL) {
1170
0
    memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1);
1171
0
    *lastgc = &lastgc1;
1172
0
  }
1173
1174
0
  len = 128;
1175
0
  buf = xmalloc(len);
1176
0
  off = 0;
1177
1178
0
  gl = grid_peek_line(gd, py);
1179
0
  if (gl == NULL) {
1180
0
    buf[0] = '\0';
1181
0
    return (buf);
1182
0
  }
1183
0
  if (flags & GRID_STRING_EMPTY_CELLS)
1184
0
    end = gl->cellsize;
1185
0
  else
1186
0
    end = gl->cellused;
1187
0
  for (xx = px; xx < px + nx; xx++) {
1188
0
    if (xx >= end)
1189
0
      break;
1190
0
    grid_get_cell(gd, xx, py, &gc);
1191
0
    if (gc.flags & GRID_FLAG_PADDING)
1192
0
      continue;
1193
1194
0
    if (lastgc != NULL && (flags & GRID_STRING_WITH_SEQUENCES)) {
1195
0
      grid_string_cells_code(*lastgc, &gc, code, sizeof code,
1196
0
          flags, s, &has_link);
1197
0
      codelen = strlen(code);
1198
0
      memcpy(*lastgc, &gc, sizeof **lastgc);
1199
0
    } else
1200
0
      codelen = 0;
1201
1202
0
    if (gc.flags & GRID_FLAG_TAB) {
1203
0
      data = "\t";
1204
0
      size = 1;
1205
0
    } else {
1206
0
      data = gc.data.data;
1207
0
      size = gc.data.size;
1208
0
      if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
1209
0
          size == 1 &&
1210
0
          *data == '\\') {
1211
0
        data = "\\\\";
1212
0
        size = 2;
1213
0
      }
1214
0
    }
1215
1216
0
    while (len < off + size + codelen + 1) {
1217
0
      buf = xreallocarray(buf, 2, len);
1218
0
      len *= 2;
1219
0
    }
1220
1221
0
    if (codelen != 0) {
1222
0
      memcpy(buf + off, code, codelen);
1223
0
      off += codelen;
1224
0
    }
1225
0
    memcpy(buf + off, data, size);
1226
0
    off += size;
1227
0
  }
1228
1229
0
  if (has_link) {
1230
0
    grid_string_cells_add_hyperlink(code, sizeof code, "", "",
1231
0
        flags);
1232
0
    codelen = strlen(code);
1233
0
    while (len < off + size + codelen + 1) {
1234
0
      buf = xreallocarray(buf, 2, len);
1235
0
      len *= 2;
1236
0
    }
1237
0
    memcpy(buf + off, code, codelen);
1238
0
    off += codelen;
1239
0
  }
1240
1241
0
  if (flags & GRID_STRING_TRIM_SPACES) {
1242
0
    while (off > 0 && buf[off - 1] == ' ')
1243
0
      off--;
1244
0
  }
1245
0
  buf[off] = '\0';
1246
1247
0
  return (buf);
1248
0
}
1249
1250
/*
1251
 * Duplicate a set of lines between two grids. Both source and destination
1252
 * should be big enough.
1253
 */
1254
void
1255
grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
1256
    u_int ny)
1257
716
{
1258
716
  struct grid_line  *dstl, *srcl;
1259
716
  u_int      yy;
1260
1261
716
  if (dy + ny > dst->hsize + dst->sy)
1262
0
    ny = dst->hsize + dst->sy - dy;
1263
716
  if (sy + ny > src->hsize + src->sy)
1264
0
    ny = src->hsize + src->sy - sy;
1265
716
  grid_free_lines(dst, dy, ny);
1266
1267
18.6k
  for (yy = 0; yy < ny; yy++) {
1268
17.9k
    srcl = &src->linedata[sy];
1269
17.9k
    dstl = &dst->linedata[dy];
1270
1271
17.9k
    memcpy(dstl, srcl, sizeof *dstl);
1272
17.9k
    if (srcl->cellsize != 0) {
1273
1.30k
      dstl->celldata = xreallocarray(NULL,
1274
1.30k
          srcl->cellsize, sizeof *dstl->celldata);
1275
1.30k
      memcpy(dstl->celldata, srcl->celldata,
1276
1.30k
          srcl->cellsize * sizeof *dstl->celldata);
1277
1.30k
    } else
1278
16.5k
      dstl->celldata = NULL;
1279
17.9k
    if (srcl->extdsize != 0) {
1280
369
      dstl->extdsize = srcl->extdsize;
1281
369
      dstl->extddata = xreallocarray(NULL, dstl->extdsize,
1282
369
          sizeof *dstl->extddata);
1283
369
      memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
1284
369
          sizeof *dstl->extddata);
1285
369
    } else
1286
17.5k
      dstl->extddata = NULL;
1287
1288
17.9k
    sy++;
1289
17.9k
    dy++;
1290
17.9k
  }
1291
716
}
1292
1293
/* Mark line as dead. */
1294
static void
1295
grid_reflow_dead(struct grid_line *gl)
1296
0
{
1297
0
  memset(gl, 0, sizeof *gl);
1298
0
  gl->flags = GRID_LINE_DEAD;
1299
0
}
1300
1301
/* Add lines, return the first new one. */
1302
static struct grid_line *
1303
grid_reflow_add(struct grid *gd, u_int n)
1304
0
{
1305
0
  struct grid_line  *gl;
1306
0
  u_int      sy = gd->sy + n;
1307
1308
0
  gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata);
1309
0
  gl = &gd->linedata[gd->sy];
1310
0
  memset(gl, 0, n * (sizeof *gl));
1311
0
  gd->sy = sy;
1312
0
  return (gl);
1313
0
}
1314
1315
/* Move a line across. */
1316
static struct grid_line *
1317
grid_reflow_move(struct grid *gd, struct grid_line *from)
1318
0
{
1319
0
  struct grid_line  *to;
1320
1321
0
  to = grid_reflow_add(gd, 1);
1322
0
  memcpy(to, from, sizeof *to);
1323
0
  grid_reflow_dead(from);
1324
0
  return (to);
1325
0
}
1326
1327
/* Join line below onto this one. */
1328
static void
1329
grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
1330
    u_int width, int already)
1331
0
{
1332
0
  struct grid_line  *gl, *from = NULL;
1333
0
  struct grid_cell   gc;
1334
0
  u_int      lines, left, i, to, line, want = 0;
1335
0
  u_int      at;
1336
0
  int      wrapped = 1;
1337
1338
  /*
1339
   * Add a new target line.
1340
   */
1341
0
  if (!already) {
1342
0
    to = target->sy;
1343
0
    gl = grid_reflow_move(target, &gd->linedata[yy]);
1344
0
  } else {
1345
0
    to = target->sy - 1;
1346
0
    gl = &target->linedata[to];
1347
0
  }
1348
0
  at = gl->cellused;
1349
1350
  /*
1351
   * Loop until no more to consume or the target line is full.
1352
   */
1353
0
  lines = 0;
1354
0
  for (;;) {
1355
    /*
1356
     * If this is now the last line, there is nothing more to be
1357
     * done.
1358
     */
1359
0
    if (yy + 1 + lines == gd->hsize + gd->sy)
1360
0
      break;
1361
0
    line = yy + 1 + lines;
1362
1363
    /* If the next line is empty, skip it. */
1364
0
    if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
1365
0
      wrapped = 0;
1366
0
    if (gd->linedata[line].cellused == 0) {
1367
0
      if (!wrapped)
1368
0
        break;
1369
0
      lines++;
1370
0
      continue;
1371
0
    }
1372
1373
    /*
1374
     * Is the destination line now full? Copy the first character
1375
     * separately because we need to leave "from" set to the last
1376
     * line if this line is full.
1377
     */
1378
0
    grid_get_cell1(&gd->linedata[line], 0, &gc);
1379
0
    if (width + gc.data.width > sx)
1380
0
      break;
1381
0
    width += gc.data.width;
1382
0
    grid_set_cell(target, at, to, &gc);
1383
0
    at++;
1384
1385
    /* Join as much more as possible onto the current line. */
1386
0
    from = &gd->linedata[line];
1387
0
    for (want = 1; want < from->cellused; want++) {
1388
0
      grid_get_cell1(from, want, &gc);
1389
0
      if (width + gc.data.width > sx)
1390
0
        break;
1391
0
      width += gc.data.width;
1392
1393
0
      grid_set_cell(target, at, to, &gc);
1394
0
      at++;
1395
0
    }
1396
0
    lines++;
1397
1398
    /*
1399
     * If this line wasn't wrapped or we didn't consume the entire
1400
     * line, don't try to join any further lines.
1401
     */
1402
0
    if (!wrapped || want != from->cellused || width == sx)
1403
0
      break;
1404
0
  }
1405
0
  if (lines == 0 || from == NULL)
1406
0
    return;
1407
1408
  /*
1409
   * If we didn't consume the entire final line, then remove what we did
1410
   * consume. If we consumed the entire line and it wasn't wrapped,
1411
   * remove the wrap flag from this line.
1412
   */
1413
0
  left = from->cellused - want;
1414
0
  if (left != 0) {
1415
0
    grid_move_cells(gd, 0, want, yy + lines, left, 8);
1416
0
    from->cellsize = from->cellused = left;
1417
0
    lines--;
1418
0
  } else if (!wrapped)
1419
0
    gl->flags &= ~GRID_LINE_WRAPPED;
1420
1421
  /* Remove the lines that were completely consumed. */
1422
0
  for (i = yy + 1; i < yy + 1 + lines; i++) {
1423
0
    free(gd->linedata[i].celldata);
1424
0
    free(gd->linedata[i].extddata);
1425
0
    grid_reflow_dead(&gd->linedata[i]);
1426
0
  }
1427
1428
  /* Adjust scroll position. */
1429
0
  if (gd->hscrolled > to + lines)
1430
0
    gd->hscrolled -= lines;
1431
0
  else if (gd->hscrolled > to)
1432
0
    gd->hscrolled = to;
1433
0
}
1434
1435
/* Split this line into several new ones */
1436
static void
1437
grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
1438
    u_int at)
1439
0
{
1440
0
  struct grid_line  *gl = &gd->linedata[yy], *first;
1441
0
  struct grid_cell   gc;
1442
0
  u_int      line, lines, width, i, xx;
1443
0
  u_int      used = gl->cellused;
1444
0
  int      flags = gl->flags;
1445
1446
  /* How many lines do we need to insert? We know we need at least two. */
1447
0
  if (~gl->flags & GRID_LINE_EXTENDED)
1448
0
    lines = 1 + (gl->cellused - 1) / sx;
1449
0
  else {
1450
0
    lines = 2;
1451
0
    width = 0;
1452
0
    for (i = at; i < used; i++) {
1453
0
      grid_get_cell1(gl, i, &gc);
1454
0
      if (width + gc.data.width > sx) {
1455
0
        lines++;
1456
0
        width = 0;
1457
0
      }
1458
0
      width += gc.data.width;
1459
0
    }
1460
0
  }
1461
1462
  /* Insert new lines. */
1463
0
  line = target->sy + 1;
1464
0
  first = grid_reflow_add(target, lines);
1465
1466
  /* Copy sections from the original line. */
1467
0
  width = 0;
1468
0
  xx = 0;
1469
0
  for (i = at; i < used; i++) {
1470
0
    grid_get_cell1(gl, i, &gc);
1471
0
    if (width + gc.data.width > sx) {
1472
0
      target->linedata[line].flags |= GRID_LINE_WRAPPED;
1473
1474
0
      line++;
1475
0
      width = 0;
1476
0
      xx = 0;
1477
0
    }
1478
0
    width += gc.data.width;
1479
0
    grid_set_cell(target, xx, line, &gc);
1480
0
    xx++;
1481
0
  }
1482
0
  if (flags & GRID_LINE_WRAPPED)
1483
0
    target->linedata[line].flags |= GRID_LINE_WRAPPED;
1484
1485
  /* Move the remainder of the original line. */
1486
0
  gl->cellsize = gl->cellused = at;
1487
0
  gl->flags |= GRID_LINE_WRAPPED;
1488
0
  memcpy(first, gl, sizeof *first);
1489
0
  grid_reflow_dead(gl);
1490
1491
  /* Adjust the scroll position. */
1492
0
  if (yy <= gd->hscrolled)
1493
0
    gd->hscrolled += lines - 1;
1494
1495
  /*
1496
   * If the original line had the wrapped flag and there is still space
1497
   * in the last new line, try to join with the next lines.
1498
   */
1499
0
  if (width < sx && (flags & GRID_LINE_WRAPPED))
1500
0
    grid_reflow_join(target, gd, sx, yy, width, 1);
1501
0
}
1502
1503
/* Reflow lines on grid to new width. */
1504
void
1505
grid_reflow(struct grid *gd, u_int sx)
1506
0
{
1507
0
  struct grid   *target;
1508
0
  struct grid_line  *gl;
1509
0
  struct grid_cell   gc;
1510
0
  u_int      yy, width, i, at;
1511
1512
  /*
1513
   * Create a destination grid. This is just used as a container for the
1514
   * line data and may not be fully valid.
1515
   */
1516
0
  target = grid_create(gd->sx, 0, 0);
1517
1518
  /*
1519
   * Loop over each source line.
1520
   */
1521
0
  for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
1522
0
    gl = &gd->linedata[yy];
1523
0
    if (gl->flags & GRID_LINE_DEAD)
1524
0
      continue;
1525
1526
    /*
1527
     * Work out the width of this line. at is the point at which
1528
     * the available width is hit, and width is the full line
1529
     * width.
1530
     */
1531
0
    at = width = 0;
1532
0
    if (~gl->flags & GRID_LINE_EXTENDED) {
1533
0
      width = gl->cellused;
1534
0
      if (width > sx)
1535
0
        at = sx;
1536
0
      else
1537
0
        at = width;
1538
0
    } else {
1539
0
      for (i = 0; i < gl->cellused; i++) {
1540
0
        grid_get_cell1(gl, i, &gc);
1541
0
        if (at == 0 && width + gc.data.width > sx)
1542
0
          at = i;
1543
0
        width += gc.data.width;
1544
0
      }
1545
0
    }
1546
1547
    /*
1548
     * If the line is exactly right, just move it across
1549
     * unchanged.
1550
     */
1551
0
    if (width == sx) {
1552
0
      grid_reflow_move(target, gl);
1553
0
      continue;
1554
0
    }
1555
1556
    /*
1557
     * If the line is too big, it needs to be split, whether or not
1558
     * it was previously wrapped.
1559
     */
1560
0
    if (width > sx) {
1561
0
      grid_reflow_split(target, gd, sx, yy, at);
1562
0
      continue;
1563
0
    }
1564
1565
    /*
1566
     * If the line was previously wrapped, join as much as possible
1567
     * of the next line.
1568
     */
1569
0
    if (gl->flags & GRID_LINE_WRAPPED)
1570
0
      grid_reflow_join(target, gd, sx, yy, width, 0);
1571
0
    else
1572
0
      grid_reflow_move(target, gl);
1573
0
  }
1574
1575
  /*
1576
   * Replace the old grid with the new.
1577
   */
1578
0
  if (target->sy < gd->sy)
1579
0
    grid_reflow_add(target, gd->sy - target->sy);
1580
0
  gd->hsize = target->sy - gd->sy;
1581
0
  if (gd->hscrolled > gd->hsize)
1582
0
    gd->hscrolled = gd->hsize;
1583
0
  free(gd->linedata);
1584
0
  gd->linedata = target->linedata;
1585
0
  free(target);
1586
0
  gd->scroll_generation++;
1587
0
}
1588
1589
/* Convert to position based on wrapped lines. */
1590
void
1591
grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy)
1592
0
{
1593
0
  u_int ax = 0, ay = 0, yy;
1594
1595
0
  for (yy = 0; yy < py; yy++) {
1596
0
    if (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
1597
0
      ax += gd->linedata[yy].cellused;
1598
0
    else {
1599
0
      ax = 0;
1600
0
      ay++;
1601
0
    }
1602
0
  }
1603
0
  if (px >= gd->linedata[yy].cellused)
1604
0
    ax = UINT_MAX;
1605
0
  else
1606
0
    ax += px;
1607
0
  *wx = ax;
1608
0
  *wy = ay;
1609
0
}
1610
1611
/* Convert position based on wrapped lines back. */
1612
void
1613
grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
1614
0
{
1615
0
  u_int yy, ay = 0;
1616
1617
0
  for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
1618
0
    if (ay == wy)
1619
0
      break;
1620
0
    if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED)
1621
0
      ay++;
1622
0
  }
1623
1624
  /*
1625
   * yy is now 0 on the unwrapped line which contains wx. Walk forwards
1626
   * until we find the end or the line now containing wx.
1627
   */
1628
0
  if (wx == UINT_MAX) {
1629
0
    while (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
1630
0
      yy++;
1631
0
    wx = gd->linedata[yy].cellused;
1632
0
  } else {
1633
0
    while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) {
1634
0
      if (wx < gd->linedata[yy].cellused)
1635
0
        break;
1636
0
      wx -= gd->linedata[yy].cellused;
1637
0
      yy++;
1638
0
    }
1639
0
  }
1640
0
  *px = wx;
1641
0
  *py = yy;
1642
0
}
1643
1644
/* Get length of line. */
1645
u_int
1646
grid_line_length(struct grid *gd, u_int py)
1647
0
{
1648
0
  struct grid_cell  gc;
1649
0
  u_int     px;
1650
1651
0
  px = grid_get_line(gd, py)->cellsize;
1652
0
  if (px > gd->sx)
1653
0
    px = gd->sx;
1654
0
  while (px > 0) {
1655
0
    grid_get_cell(gd, px - 1, py, &gc);
1656
0
    if ((gc.flags & GRID_FLAG_PADDING) ||
1657
0
        gc.data.size != 1 ||
1658
0
        *gc.data.data != ' ')
1659
0
      break;
1660
0
    px--;
1661
0
  }
1662
0
  return (px);
1663
0
}
1664
1665
/* Check if character is in set. */
1666
int
1667
grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
1668
0
{
1669
0
  struct grid_cell  gc, tmp_gc;
1670
0
  u_int     pxx;
1671
1672
0
  grid_get_cell(gd, px, py, &gc);
1673
0
  if (strchr(set, '\t')) {
1674
0
    if (gc.flags & GRID_FLAG_PADDING) {
1675
0
      pxx = px;
1676
0
      do
1677
0
        grid_get_cell(gd, --pxx, py, &tmp_gc);
1678
0
      while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING);
1679
0
      if (tmp_gc.flags & GRID_FLAG_TAB)
1680
0
        return (tmp_gc.data.width - (px - pxx));
1681
0
    } else if (gc.flags & GRID_FLAG_TAB)
1682
0
      return (gc.data.width);
1683
0
  }
1684
0
  if (gc.flags & GRID_FLAG_PADDING)
1685
0
    return (0);
1686
0
  return (utf8_cstrhas(set, &gc.data));
1687
0
}
1688
1689
/* Line flags to string. */
1690
const char *
1691
grid_line_flags_string(int flags)
1692
0
{
1693
0
  static char s[128];
1694
1695
0
  *s = '\0';
1696
0
  if (flags & GRID_LINE_WRAPPED)
1697
0
    strlcat(s, "WRAPPED,", sizeof s);
1698
0
  if (flags & GRID_LINE_EXTENDED)
1699
0
    strlcat(s, "EXTENDED,", sizeof s);
1700
0
  if (flags & GRID_LINE_DEAD)
1701
0
    strlcat(s, "DEAD,", sizeof s);
1702
0
  if (flags & GRID_LINE_START_PROMPT)
1703
0
    strlcat(s, "START_PROMPT,", sizeof s);
1704
0
  if (flags & GRID_LINE_START_OUTPUT)
1705
0
    strlcat(s, "START_OUTPUT,", sizeof s);
1706
0
  if (flags & GRID_LINE_HYPERLINK)
1707
0
    strlcat(s, "HYPERLINK,", sizeof s);
1708
0
  if (*s == '\0')
1709
0
    return ("NONE");
1710
0
  s[strlen(s) - 1] = '\0';
1711
0
  return (s);
1712
0
}
1713
1714
/* Cell flags to string. */
1715
const char *
1716
grid_cell_flags_string(int flags)
1717
0
{
1718
0
  static char s[128];
1719
1720
0
  *s = '\0';
1721
0
  if (flags & GRID_FLAG_FG256)
1722
0
    strlcat(s, "FG256,", sizeof s);
1723
0
  if (flags & GRID_FLAG_BG256)
1724
0
    strlcat(s, "BG256,", sizeof s);
1725
0
  if (flags & GRID_FLAG_PADDING)
1726
0
    strlcat(s, "PADDING,", sizeof s);
1727
0
  if (flags & GRID_FLAG_EXTENDED)
1728
0
    strlcat(s, "EXTENDED,", sizeof s);
1729
0
  if (flags & GRID_FLAG_SELECTED)
1730
0
    strlcat(s, "SELECTED,", sizeof s);
1731
0
  if (flags & GRID_FLAG_CLEARED)
1732
0
    strlcat(s, "CLEARED,", sizeof s);
1733
0
  if (flags & GRID_FLAG_TAB)
1734
0
    strlcat(s, "TAB,", sizeof s);
1735
0
  if (flags & GRID_FLAG_NOPALETTE)
1736
0
    strlcat(s, "NOPALETTE,", sizeof s);
1737
0
  if (*s == '\0')
1738
0
    return ("NONE");
1739
0
  s[strlen(s) - 1] = '\0';
1740
0
  return (s);
1741
0
}
1742
1743
/* Cell attributes to string. */
1744
const char *
1745
grid_cell_attr_string(int attr)
1746
0
{
1747
0
  static char s[256];
1748
1749
0
  *s = '\0';
1750
0
  if (attr & GRID_ATTR_CHARSET)
1751
0
    strlcat(s, "CHARSET,", sizeof s);
1752
0
  if (attr & GRID_ATTR_BRIGHT)
1753
0
    strlcat(s, "BRIGHT,", sizeof s);
1754
0
  if (attr & GRID_ATTR_DIM)
1755
0
    strlcat(s, "DIM,", sizeof s);
1756
0
  if (attr & GRID_ATTR_UNDERSCORE)
1757
0
    strlcat(s, "UNDERSCORE,", sizeof s);
1758
0
  if (attr & GRID_ATTR_BLINK)
1759
0
    strlcat(s, "BLINK,", sizeof s);
1760
0
  if (attr & GRID_ATTR_REVERSE)
1761
0
    strlcat(s, "REVERSE,", sizeof s);
1762
0
  if (attr & GRID_ATTR_HIDDEN)
1763
0
    strlcat(s, "HIDDEN,", sizeof s);
1764
0
  if (attr & GRID_ATTR_ITALICS)
1765
0
    strlcat(s, "ITALICS,", sizeof s);
1766
0
  if (attr & GRID_ATTR_STRIKETHROUGH)
1767
0
    strlcat(s, "STRIKETHROUGH,", sizeof s);
1768
0
  if (attr & GRID_ATTR_UNDERSCORE_2)
1769
0
    strlcat(s, "UNDERSCORE_2,", sizeof s);
1770
0
  if (attr & GRID_ATTR_UNDERSCORE_3)
1771
0
    strlcat(s, "UNDERSCORE_3,", sizeof s);
1772
0
  if (attr & GRID_ATTR_UNDERSCORE_4)
1773
0
    strlcat(s, "UNDERSCORE_4,", sizeof s);
1774
0
  if (attr & GRID_ATTR_UNDERSCORE_5)
1775
0
    strlcat(s, "UNDERSCORE_5,", sizeof s);
1776
0
  if (attr & GRID_ATTR_OVERLINE)
1777
0
    strlcat(s, "OVERLINE,", sizeof s);
1778
0
  if (*s == '\0')
1779
0
    return ("NONE");
1780
0
  s[strlen(s) - 1] = '\0';
1781
0
  return (s);
1782
0
}