GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/grid.c Lines: 0 476 0.0 %
Date: 2017-11-13 Branches: 0 346 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: grid.c,v 1.78 2017/11/03 17:02:33 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
#include <stdlib.h>
22
#include <string.h>
23
24
#include "tmux.h"
25
26
/*
27
 * Grid data. This is the basic data structure that represents what is shown on
28
 * screen.
29
 *
30
 * A grid is a grid of cells (struct grid_cell). Lines are not allocated until
31
 * cells in that line are written to. The grid is split into history and
32
 * viewable data with the history starting at row (line) 0 and extending to
33
 * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All
34
 * functions in this file work on absolute coordinates, grid-view.c has
35
 * functions which work on the screen data.
36
 */
37
38
/* Default grid cell data. */
39
const struct grid_cell grid_default_cell = {
40
	0, 0, 8, 8, { { ' ' }, 0, 1, 1 }
41
};
42
static const struct grid_cell_entry grid_default_entry = {
43
	0, { .data = { 0, 8, 8, ' ' } }
44
};
45
46
static void	grid_expand_line(struct grid *, u_int, u_int, u_int);
47
static void	grid_empty_line(struct grid *, u_int, u_int);
48
49
static void	grid_reflow_copy(struct grid_line *, u_int, struct grid_line *,
50
		    u_int, u_int);
51
static void	grid_reflow_join(struct grid *, u_int *, struct grid_line *,
52
		    u_int);
53
static void	grid_reflow_split(struct grid *, u_int *, struct grid_line *,
54
		    u_int, u_int);
55
static void	grid_reflow_move(struct grid *, u_int *, struct grid_line *);
56
57
static size_t	grid_string_cells_fg(const struct grid_cell *, int *);
58
static size_t	grid_string_cells_bg(const struct grid_cell *, int *);
59
static void	grid_string_cells_code(const struct grid_cell *,
60
		    const struct grid_cell *, char *, size_t, int);
61
62
/* Store cell in entry. */
63
static void
64
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
65
    u_char c)
66
{
67
	gce->flags = gc->flags;
68
69
	gce->data.fg = gc->fg & 0xff;
70
	if (gc->fg & COLOUR_FLAG_256)
71
		gce->flags |= GRID_FLAG_FG256;
72
73
	gce->data.bg = gc->bg & 0xff;
74
	if (gc->bg & COLOUR_FLAG_256)
75
		gce->flags |= GRID_FLAG_BG256;
76
77
	gce->data.attr = gc->attr;
78
	gce->data.data = c;
79
}
80
81
/* Check if a cell should be extended. */
82
static int
83
grid_need_extended_cell(const struct grid_cell_entry *gce,
84
    const struct grid_cell *gc)
85
{
86
	if (gce->flags & GRID_FLAG_EXTENDED)
87
		return (1);
88
	if (gc->attr > 0xff)
89
		return (1);
90
	if (gc->data.size != 1 || gc->data.width != 1)
91
		return (1);
92
	if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
93
		return (1);
94
	return (0);
95
}
96
97
/* Free up unused extended cells. */
98
static void
99
grid_compact_line(struct grid_line *gl)
100
{
101
	int			 new_extdsize = 0;
102
	struct grid_cell	*new_extddata;
103
	struct grid_cell_entry	*gce;
104
	struct grid_cell	*gc;
105
	u_int			 px, idx;
106
107
	if (gl->extdsize == 0)
108
		return;
109
110
	for (px = 0; px < gl->cellsize; px++) {
111
		gce = &gl->celldata[px];
112
		if (gce->flags & GRID_FLAG_EXTENDED)
113
			new_extdsize++;
114
	}
115
116
	if (new_extdsize == 0) {
117
		free(gl->extddata);
118
		gl->extddata = NULL;
119
		gl->extdsize = 0;
120
		return;
121
	}
122
	new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata);
123
124
	idx = 0;
125
	for (px = 0; px < gl->cellsize; px++) {
126
		gce = &gl->celldata[px];
127
		if (gce->flags & GRID_FLAG_EXTENDED) {
128
			gc = &gl->extddata[gce->offset];
129
			memcpy(&new_extddata[idx], gc, sizeof *gc);
130
			gce->offset = idx++;
131
		}
132
	}
133
134
	free(gl->extddata);
135
	gl->extddata = new_extddata;
136
	gl->extdsize = new_extdsize;
137
}
138
139
/* Set cell as extended. */
140
static struct grid_cell *
141
grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
142
    const struct grid_cell *gc)
143
{
144
	struct grid_cell	*gcp;
145
146
	gl->flags |= GRID_LINE_EXTENDED;
147
148
	if (~gce->flags & GRID_FLAG_EXTENDED) {
149
		gl->extddata = xreallocarray(gl->extddata, gl->extdsize + 1,
150
		    sizeof *gl->extddata);
151
		gce->offset = gl->extdsize++;
152
		gce->flags = gc->flags | GRID_FLAG_EXTENDED;
153
	}
154
	if (gce->offset >= gl->extdsize)
155
		fatalx("offset too big");
156
157
	gcp = &gl->extddata[gce->offset];
158
	memcpy(gcp, gc, sizeof *gcp);
159
	return (gcp);
160
}
161
162
/* Copy default into a cell. */
163
static void
164
grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
165
{
166
	struct grid_line	*gl = &gd->linedata[py];
167
	struct grid_cell_entry	*gce = &gl->celldata[px];
168
	struct grid_cell	*gc;
169
170
	memcpy(gce, &grid_default_entry, sizeof *gce);
171
	if (bg & COLOUR_FLAG_RGB) {
172
		gc = grid_extended_cell(gl, gce, &grid_default_cell);
173
		gc->bg = bg;
174
	} else {
175
		if (bg & COLOUR_FLAG_256)
176
			gce->flags |= GRID_FLAG_BG256;
177
		gce->data.bg = bg;
178
	}
179
}
180
181
/* Check grid y position. */
182
static int
183
grid_check_y(struct grid *gd, u_int py)
184
{
185
	if (py >= gd->hsize + gd->sy) {
186
		log_debug("y out of range: %u", py);
187
		return (-1);
188
	}
189
	return (0);
190
}
191
192
/* Compare grid cells. Return 1 if equal, 0 if not. */
193
int
194
grid_cells_equal(const struct grid_cell *gca, const struct grid_cell *gcb)
195
{
196
	if (gca->fg != gcb->fg || gca->bg != gcb->bg)
197
		return (0);
198
	if (gca->attr != gcb->attr || gca->flags != gcb->flags)
199
		return (0);
200
	if (gca->data.width != gcb->data.width)
201
		return (0);
202
	if (gca->data.size != gcb->data.size)
203
		return (0);
204
	return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0);
205
}
206
207
/* Free one line. */
208
static void
209
grid_free_line(struct grid *gd, u_int py)
210
{
211
	free(gd->linedata[py].celldata);
212
	gd->linedata[py].celldata = NULL;
213
	free(gd->linedata[py].extddata);
214
	gd->linedata[py].extddata = NULL;
215
}
216
217
/* Free several lines. */
218
static void
219
grid_free_lines(struct grid *gd, u_int py, u_int ny)
220
{
221
	u_int	yy;
222
223
	for (yy = py; yy < py + ny; yy++)
224
		grid_free_line(gd, yy);
225
}
226
227
/* Create a new grid. */
228
struct grid *
229
grid_create(u_int sx, u_int sy, u_int hlimit)
230
{
231
	struct grid	*gd;
232
233
	gd = xmalloc(sizeof *gd);
234
	gd->sx = sx;
235
	gd->sy = sy;
236
237
	gd->flags = GRID_HISTORY;
238
239
	gd->hscrolled = 0;
240
	gd->hsize = 0;
241
	gd->hlimit = hlimit;
242
243
	gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
244
245
	return (gd);
246
}
247
248
/* Destroy grid. */
249
void
250
grid_destroy(struct grid *gd)
251
{
252
	grid_free_lines(gd, 0, gd->hsize + gd->sy);
253
254
	free(gd->linedata);
255
256
	free(gd);
257
}
258
259
/* Compare grids. */
260
int
261
grid_compare(struct grid *ga, struct grid *gb)
262
{
263
	struct grid_line	*gla, *glb;
264
	struct grid_cell	 gca, gcb;
265
	u_int			 xx, yy;
266
267
	if (ga->sx != gb->sx || ga->sy != gb->sy)
268
		return (1);
269
270
	for (yy = 0; yy < ga->sy; yy++) {
271
		gla = &ga->linedata[yy];
272
		glb = &gb->linedata[yy];
273
		if (gla->cellsize != glb->cellsize)
274
			return (1);
275
		for (xx = 0; xx < gla->cellsize; xx++) {
276
			grid_get_cell(ga, xx, yy, &gca);
277
			grid_get_cell(gb, xx, yy, &gcb);
278
			if (!grid_cells_equal(&gca, &gcb))
279
				return (1);
280
		}
281
	}
282
283
	return (0);
284
}
285
286
/*
287
 * Collect lines from the history if at the limit. Free the top (oldest) 10%
288
 * and shift up.
289
 */
290
void
291
grid_collect_history(struct grid *gd)
292
{
293
	u_int	ny;
294
295
	if (gd->hsize == 0 || gd->hsize < gd->hlimit)
296
		return;
297
298
	ny = gd->hlimit / 10;
299
	if (ny < 1)
300
		ny = 1;
301
	if (ny > gd->hsize)
302
		ny = gd->hsize;
303
304
	/*
305
	 * Free the lines from 0 to ny then move the remaining lines over
306
	 * them.
307
	 */
308
	grid_free_lines(gd, 0, ny);
309
	memmove(&gd->linedata[0], &gd->linedata[ny],
310
	    (gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
311
312
	gd->hsize -= ny;
313
	if (gd->hscrolled > gd->hsize)
314
		gd->hscrolled = gd->hsize;
315
}
316
317
/*
318
 * Scroll the entire visible screen, moving one line into the history. Just
319
 * allocate a new line at the bottom and move the history size indicator.
320
 */
321
void
322
grid_scroll_history(struct grid *gd, u_int bg)
323
{
324
	u_int	yy;
325
326
	yy = gd->hsize + gd->sy;
327
	gd->linedata = xreallocarray(gd->linedata, yy + 1,
328
	    sizeof *gd->linedata);
329
	grid_empty_line(gd, yy, bg);
330
331
	gd->hscrolled++;
332
	grid_compact_line(&gd->linedata[gd->hsize]);
333
	gd->hsize++;
334
}
335
336
/* Clear the history. */
337
void
338
grid_clear_history(struct grid *gd)
339
{
340
	grid_free_lines(gd, 0, gd->hsize);
341
	memmove(&gd->linedata[0], &gd->linedata[gd->hsize],
342
	    gd->sy * (sizeof *gd->linedata));
343
344
	gd->hscrolled = 0;
345
	gd->hsize = 0;
346
347
	gd->linedata = xreallocarray(gd->linedata, gd->sy,
348
	    sizeof *gd->linedata);
349
}
350
351
/* Scroll a region up, moving the top line into the history. */
352
void
353
grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
354
{
355
	struct grid_line	*gl_history, *gl_upper;
356
	u_int			 yy;
357
358
	/* Create a space for a new line. */
359
	yy = gd->hsize + gd->sy;
360
	gd->linedata = xreallocarray(gd->linedata, yy + 1,
361
	    sizeof *gd->linedata);
362
363
	/* Move the entire screen down to free a space for this line. */
364
	gl_history = &gd->linedata[gd->hsize];
365
	memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);
366
367
	/* Adjust the region and find its start and end. */
368
	upper++;
369
	gl_upper = &gd->linedata[upper];
370
	lower++;
371
372
	/* Move the line into the history. */
373
	memcpy(gl_history, gl_upper, sizeof *gl_history);
374
375
	/* Then move the region up and clear the bottom line. */
376
	memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
377
	grid_empty_line(gd, lower, bg);
378
379
	/* Move the history offset down over the line. */
380
	gd->hscrolled++;
381
	gd->hsize++;
382
}
383
384
/* Expand line to fit to cell. */
385
static void
386
grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
387
{
388
	struct grid_line	*gl;
389
	u_int			 xx;
390
391
	gl = &gd->linedata[py];
392
	if (sx <= gl->cellsize)
393
		return;
394
395
	if (sx < gd->sx / 4)
396
		sx = gd->sx / 4;
397
	else if (sx < gd->sx / 2)
398
		sx = gd->sx / 2;
399
	else
400
		sx = gd->sx;
401
402
	gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
403
	for (xx = gl->cellsize; xx < sx; xx++)
404
		grid_clear_cell(gd, xx, py, bg);
405
	gl->cellsize = sx;
406
}
407
408
/* Empty a line and set background colour if needed. */
409
static void
410
grid_empty_line(struct grid *gd, u_int py, u_int bg)
411
{
412
	memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
413
	if (bg != 8)
414
		grid_expand_line(gd, py, gd->sx, bg);
415
}
416
417
/* Peek at grid line. */
418
const struct grid_line *
419
grid_peek_line(struct grid *gd, u_int py)
420
{
421
	if (grid_check_y(gd, py) != 0)
422
		return (NULL);
423
	return (&gd->linedata[py]);
424
}
425
426
/* Get cell for reading. */
427
void
428
grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
429
{
430
	struct grid_line	*gl;
431
	struct grid_cell_entry	*gce;
432
433
	if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) {
434
		memcpy(gc, &grid_default_cell, sizeof *gc);
435
		return;
436
	}
437
438
	gl = &gd->linedata[py];
439
	gce = &gl->celldata[px];
440
441
	if (gce->flags & GRID_FLAG_EXTENDED) {
442
		if (gce->offset >= gl->extdsize)
443
			memcpy(gc, &grid_default_cell, sizeof *gc);
444
		else
445
			memcpy(gc, &gl->extddata[gce->offset], sizeof *gc);
446
		return;
447
	}
448
449
	gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
450
	gc->attr = gce->data.attr;
451
	gc->fg = gce->data.fg;
452
	if (gce->flags & GRID_FLAG_FG256)
453
		gc->fg |= COLOUR_FLAG_256;
454
	gc->bg = gce->data.bg;
455
	if (gce->flags & GRID_FLAG_BG256)
456
		gc->bg |= COLOUR_FLAG_256;
457
	utf8_set(&gc->data, gce->data.data);
458
}
459
460
/* Set cell at relative position. */
461
void
462
grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
463
{
464
	struct grid_line	*gl;
465
	struct grid_cell_entry	*gce;
466
467
	if (grid_check_y(gd, py) != 0)
468
		return;
469
470
	grid_expand_line(gd, py, px + 1, 8);
471
472
	gl = &gd->linedata[py];
473
	if (px + 1 > gl->cellused)
474
		gl->cellused = px + 1;
475
476
	gce = &gl->celldata[px];
477
	if (grid_need_extended_cell(gce, gc))
478
		grid_extended_cell(gl, gce, gc);
479
	else
480
		grid_store_cell(gce, gc, gc->data.data[0]);
481
}
482
483
/* Set cells at relative position. */
484
void
485
grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
486
    const char *s, size_t slen)
487
{
488
	struct grid_line	*gl;
489
	struct grid_cell_entry	*gce;
490
	struct grid_cell	*gcp;
491
	u_int			 i;
492
493
	if (grid_check_y(gd, py) != 0)
494
		return;
495
496
	grid_expand_line(gd, py, px + slen, 8);
497
498
	gl = &gd->linedata[py];
499
	if (px + slen > gl->cellused)
500
		gl->cellused = px + slen;
501
502
	for (i = 0; i < slen; i++) {
503
		gce = &gl->celldata[px + i];
504
		if (grid_need_extended_cell(gce, gc)) {
505
			gcp = grid_extended_cell(gl, gce, gc);
506
			utf8_set(&gcp->data, s[i]);
507
		} else
508
			grid_store_cell(gce, gc, s[i]);
509
	}
510
}
511
512
/* Clear area. */
513
void
514
grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
515
{
516
	u_int	xx, yy;
517
518
	if (nx == 0 || ny == 0)
519
		return;
520
521
	if (px == 0 && nx == gd->sx) {
522
		grid_clear_lines(gd, py, ny, bg);
523
		return;
524
	}
525
526
	if (grid_check_y(gd, py) != 0)
527
		return;
528
	if (grid_check_y(gd, py + ny - 1) != 0)
529
		return;
530
531
	for (yy = py; yy < py + ny; yy++) {
532
		if (px + nx >= gd->sx && px < gd->linedata[yy].cellused)
533
			gd->linedata[yy].cellused = px;
534
		if (px > gd->linedata[yy].cellsize && bg == 8)
535
			continue;
536
		if (px + nx >= gd->linedata[yy].cellsize && bg == 8) {
537
			gd->linedata[yy].cellsize = px;
538
			continue;
539
		}
540
		grid_expand_line(gd, yy, px + nx, 8); /* default bg first */
541
		for (xx = px; xx < px + nx; xx++)
542
			grid_clear_cell(gd, xx, yy, bg);
543
	}
544
}
545
546
/* Clear lines. This just frees and truncates the lines. */
547
void
548
grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
549
{
550
	u_int	yy;
551
552
	if (ny == 0)
553
		return;
554
555
	if (grid_check_y(gd, py) != 0)
556
		return;
557
	if (grid_check_y(gd, py + ny - 1) != 0)
558
		return;
559
560
	for (yy = py; yy < py + ny; yy++) {
561
		grid_free_line(gd, yy);
562
		grid_empty_line(gd, yy, bg);
563
	}
564
}
565
566
/* Move a group of lines. */
567
void
568
grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
569
{
570
	u_int	yy;
571
572
	if (ny == 0 || py == dy)
573
		return;
574
575
	if (grid_check_y(gd, py) != 0)
576
		return;
577
	if (grid_check_y(gd, py + ny - 1) != 0)
578
		return;
579
	if (grid_check_y(gd, dy) != 0)
580
		return;
581
	if (grid_check_y(gd, dy + ny - 1) != 0)
582
		return;
583
584
	/* Free any lines which are being replaced. */
585
	for (yy = dy; yy < dy + ny; yy++) {
586
		if (yy >= py && yy < py + ny)
587
			continue;
588
		grid_free_line(gd, yy);
589
	}
590
591
	memmove(&gd->linedata[dy], &gd->linedata[py],
592
	    ny * (sizeof *gd->linedata));
593
594
	/*
595
	 * Wipe any lines that have been moved (without freeing them - they are
596
	 * still present).
597
	 */
598
	for (yy = py; yy < py + ny; yy++) {
599
		if (yy < dy || yy >= dy + ny)
600
			grid_empty_line(gd, yy, bg);
601
	}
602
}
603
604
/* Move a group of cells. */
605
void
606
grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
607
    u_int bg)
608
{
609
	struct grid_line	*gl;
610
	u_int			 xx;
611
612
	if (nx == 0 || px == dx)
613
		return;
614
615
	if (grid_check_y(gd, py) != 0)
616
		return;
617
	gl = &gd->linedata[py];
618
619
	grid_expand_line(gd, py, px + nx, 8);
620
	grid_expand_line(gd, py, dx + nx, 8);
621
	memmove(&gl->celldata[dx], &gl->celldata[px],
622
	    nx * sizeof *gl->celldata);
623
	if (dx + nx > gl->cellused)
624
		gl->cellused = dx + nx;
625
626
	/* Wipe any cells that have been moved. */
627
	for (xx = px; xx < px + nx; xx++) {
628
		if (xx >= dx && xx < dx + nx)
629
			continue;
630
		grid_clear_cell(gd, xx, py, bg);
631
	}
632
}
633
634
/* Get ANSI foreground sequence. */
635
static size_t
636
grid_string_cells_fg(const struct grid_cell *gc, int *values)
637
{
638
	size_t	n;
639
	u_char	r, g, b;
640
641
	n = 0;
642
	if (gc->fg & COLOUR_FLAG_256) {
643
		values[n++] = 38;
644
		values[n++] = 5;
645
		values[n++] = gc->fg & 0xff;
646
	} else if (gc->fg & COLOUR_FLAG_RGB) {
647
		values[n++] = 38;
648
		values[n++] = 2;
649
		colour_split_rgb(gc->fg, &r, &g, &b);
650
		values[n++] = r;
651
		values[n++] = g;
652
		values[n++] = b;
653
	} else {
654
		switch (gc->fg) {
655
		case 0:
656
		case 1:
657
		case 2:
658
		case 3:
659
		case 4:
660
		case 5:
661
		case 6:
662
		case 7:
663
			values[n++] = gc->fg + 30;
664
			break;
665
		case 8:
666
			values[n++] = 39;
667
			break;
668
		case 90:
669
		case 91:
670
		case 92:
671
		case 93:
672
		case 94:
673
		case 95:
674
		case 96:
675
		case 97:
676
			values[n++] = gc->fg;
677
			break;
678
		}
679
	}
680
	return (n);
681
}
682
683
/* Get ANSI background sequence. */
684
static size_t
685
grid_string_cells_bg(const struct grid_cell *gc, int *values)
686
{
687
	size_t	n;
688
	u_char	r, g, b;
689
690
	n = 0;
691
	if (gc->bg & COLOUR_FLAG_256) {
692
		values[n++] = 48;
693
		values[n++] = 5;
694
		values[n++] = gc->bg & 0xff;
695
	} else if (gc->bg & COLOUR_FLAG_RGB) {
696
		values[n++] = 48;
697
		values[n++] = 2;
698
		colour_split_rgb(gc->bg, &r, &g, &b);
699
		values[n++] = r;
700
		values[n++] = g;
701
		values[n++] = b;
702
	} else {
703
		switch (gc->bg) {
704
		case 0:
705
		case 1:
706
		case 2:
707
		case 3:
708
		case 4:
709
		case 5:
710
		case 6:
711
		case 7:
712
			values[n++] = gc->bg + 40;
713
			break;
714
		case 8:
715
			values[n++] = 49;
716
			break;
717
		case 100:
718
		case 101:
719
		case 102:
720
		case 103:
721
		case 104:
722
		case 105:
723
		case 106:
724
		case 107:
725
			values[n++] = gc->bg - 10;
726
			break;
727
		}
728
	}
729
	return (n);
730
}
731
732
/*
733
 * Returns ANSI code to set particular attributes (colour, bold and so on)
734
 * given a current state.
735
 */
736
static void
737
grid_string_cells_code(const struct grid_cell *lastgc,
738
    const struct grid_cell *gc, char *buf, size_t len, int escape_c0)
739
{
740
	int	oldc[64], newc[64], s[128];
741
	size_t	noldc, nnewc, n, i;
742
	u_int	attr = gc->attr, lastattr = lastgc->attr;
743
	char	tmp[64];
744
745
	struct {
746
		u_int	mask;
747
		u_int	code;
748
	} attrs[] = {
749
		{ GRID_ATTR_BRIGHT, 1 },
750
		{ GRID_ATTR_DIM, 2 },
751
		{ GRID_ATTR_ITALICS, 3 },
752
		{ GRID_ATTR_UNDERSCORE, 4 },
753
		{ GRID_ATTR_BLINK, 5 },
754
		{ GRID_ATTR_REVERSE, 7 },
755
		{ GRID_ATTR_HIDDEN, 8 },
756
		{ GRID_ATTR_STRIKETHROUGH, 9 }
757
	};
758
	n = 0;
759
760
	/* If any attribute is removed, begin with 0. */
761
	for (i = 0; i < nitems(attrs); i++) {
762
		if (!(attr & attrs[i].mask) && (lastattr & attrs[i].mask)) {
763
			s[n++] = 0;
764
			lastattr &= GRID_ATTR_CHARSET;
765
			break;
766
		}
767
	}
768
	/* For each attribute that is newly set, add its code. */
769
	for (i = 0; i < nitems(attrs); i++) {
770
		if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask))
771
			s[n++] = attrs[i].code;
772
	}
773
774
	/* Write the attributes. */
775
	*buf = '\0';
776
	if (n > 0) {
777
		if (escape_c0)
778
			strlcat(buf, "\\033[", len);
779
		else
780
			strlcat(buf, "\033[", len);
781
		for (i = 0; i < n; i++) {
782
			if (i + 1 < n)
783
				xsnprintf(tmp, sizeof tmp, "%d;", s[i]);
784
			else
785
				xsnprintf(tmp, sizeof tmp, "%d", s[i]);
786
			strlcat(buf, tmp, len);
787
		}
788
		strlcat(buf, "m", len);
789
	}
790
791
	/* If the foreground colour changed, write its parameters. */
792
	nnewc = grid_string_cells_fg(gc, newc);
793
	noldc = grid_string_cells_fg(lastgc, oldc);
794
	if (nnewc != noldc ||
795
	    memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 ||
796
	    (n != 0 && s[0] == 0)) {
797
		if (escape_c0)
798
			strlcat(buf, "\\033[", len);
799
		else
800
			strlcat(buf, "\033[", len);
801
		for (i = 0; i < nnewc; i++) {
802
			if (i + 1 < nnewc)
803
				xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
804
			else
805
				xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
806
			strlcat(buf, tmp, len);
807
		}
808
		strlcat(buf, "m", len);
809
	}
810
811
	/* If the background colour changed, append its parameters. */
812
	nnewc = grid_string_cells_bg(gc, newc);
813
	noldc = grid_string_cells_bg(lastgc, oldc);
814
	if (nnewc != noldc ||
815
	    memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 ||
816
	    (n != 0 && s[0] == 0)) {
817
		if (escape_c0)
818
			strlcat(buf, "\\033[", len);
819
		else
820
			strlcat(buf, "\033[", len);
821
		for (i = 0; i < nnewc; i++) {
822
			if (i + 1 < nnewc)
823
				xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
824
			else
825
				xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
826
			strlcat(buf, tmp, len);
827
		}
828
		strlcat(buf, "m", len);
829
	}
830
831
	/* Append shift in/shift out if needed. */
832
	if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
833
		if (escape_c0)
834
			strlcat(buf, "\\016", len); /* SO */
835
		else
836
			strlcat(buf, "\016", len);  /* SO */
837
	}
838
	if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
839
		if (escape_c0)
840
			strlcat(buf, "\\017", len); /* SI */
841
		else
842
			strlcat(buf, "\017", len);  /* SI */
843
	}
844
}
845
846
/* Convert cells into a string. */
847
char *
848
grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
849
    struct grid_cell **lastgc, int with_codes, int escape_c0, int trim)
850
{
851
	struct grid_cell	 gc;
852
	static struct grid_cell	 lastgc1;
853
	const char		*data;
854
	char			*buf, code[128];
855
	size_t			 len, off, size, codelen;
856
	u_int			 xx;
857
	const struct grid_line	*gl;
858
859
	if (lastgc != NULL && *lastgc == NULL) {
860
		memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1);
861
		*lastgc = &lastgc1;
862
	}
863
864
	len = 128;
865
	buf = xmalloc(len);
866
	off = 0;
867
868
	gl = grid_peek_line(gd, py);
869
	for (xx = px; xx < px + nx; xx++) {
870
		if (gl == NULL || xx >= gl->cellsize)
871
			break;
872
		grid_get_cell(gd, xx, py, &gc);
873
		if (gc.flags & GRID_FLAG_PADDING)
874
			continue;
875
876
		if (with_codes) {
877
			grid_string_cells_code(*lastgc, &gc, code, sizeof code,
878
			    escape_c0);
879
			codelen = strlen(code);
880
			memcpy(*lastgc, &gc, sizeof **lastgc);
881
		} else
882
			codelen = 0;
883
884
		data = gc.data.data;
885
		size = gc.data.size;
886
		if (escape_c0 && size == 1 && *data == '\\') {
887
			data = "\\\\";
888
			size = 2;
889
		}
890
891
		while (len < off + size + codelen + 1) {
892
			buf = xreallocarray(buf, 2, len);
893
			len *= 2;
894
		}
895
896
		if (codelen != 0) {
897
			memcpy(buf + off, code, codelen);
898
			off += codelen;
899
		}
900
		memcpy(buf + off, data, size);
901
		off += size;
902
	}
903
904
	if (trim) {
905
		while (off > 0 && buf[off - 1] == ' ')
906
			off--;
907
	}
908
	buf[off] = '\0';
909
910
	return (buf);
911
}
912
913
/*
914
 * Duplicate a set of lines between two grids. Both source and destination
915
 * should be big enough.
916
 */
917
void
918
grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
919
    u_int ny)
920
{
921
	struct grid_line	*dstl, *srcl;
922
	u_int			 yy;
923
924
	if (dy + ny > dst->hsize + dst->sy)
925
		ny = dst->hsize + dst->sy - dy;
926
	if (sy + ny > src->hsize + src->sy)
927
		ny = src->hsize + src->sy - sy;
928
	grid_free_lines(dst, dy, ny);
929
930
	for (yy = 0; yy < ny; yy++) {
931
		srcl = &src->linedata[sy];
932
		dstl = &dst->linedata[dy];
933
934
		memcpy(dstl, srcl, sizeof *dstl);
935
		if (srcl->cellsize != 0) {
936
			dstl->celldata = xreallocarray(NULL,
937
			    srcl->cellsize, sizeof *dstl->celldata);
938
			memcpy(dstl->celldata, srcl->celldata,
939
			    srcl->cellsize * sizeof *dstl->celldata);
940
		} else
941
			dstl->celldata = NULL;
942
943
		if (srcl->extdsize != 0) {
944
			dstl->extdsize = srcl->extdsize;
945
			dstl->extddata = xreallocarray(NULL, dstl->extdsize,
946
			    sizeof *dstl->extddata);
947
			memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
948
			    sizeof *dstl->extddata);
949
		}
950
951
		sy++;
952
		dy++;
953
	}
954
}
955
956
/* Copy a section of a line. */
957
static void
958
grid_reflow_copy(struct grid_line *dst_gl, u_int to, struct grid_line *src_gl,
959
    u_int from, u_int to_copy)
960
{
961
	struct grid_cell_entry	*gce;
962
	u_int			 i, was;
963
964
	memcpy(&dst_gl->celldata[to], &src_gl->celldata[from],
965
	    to_copy * sizeof *dst_gl->celldata);
966
967
	for (i = to; i < to + to_copy; i++) {
968
		gce = &dst_gl->celldata[i];
969
		if (~gce->flags & GRID_FLAG_EXTENDED)
970
			continue;
971
		was = gce->offset;
972
973
		dst_gl->extddata = xreallocarray(dst_gl->extddata,
974
		    dst_gl->extdsize + 1, sizeof *dst_gl->extddata);
975
		gce->offset = dst_gl->extdsize++;
976
		memcpy(&dst_gl->extddata[gce->offset], &src_gl->extddata[was],
977
		    sizeof *dst_gl->extddata);
978
	}
979
}
980
981
/* Join line data. */
982
static void
983
grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl,
984
    u_int new_x)
985
{
986
	struct grid_line	*dst_gl = &dst->linedata[(*py) - 1];
987
	u_int			 left, to_copy, ox, nx;
988
989
	/* How much is left on the old line? */
990
	left = new_x - dst_gl->cellused;
991
992
	/* Work out how much to append. */
993
	to_copy = src_gl->cellused;
994
	if (to_copy > left)
995
		to_copy = left;
996
	ox = dst_gl->cellused;
997
	nx = ox + to_copy;
998
999
	/* Resize the destination line. */
1000
	dst_gl->celldata = xreallocarray(dst_gl->celldata, nx,
1001
	    sizeof *dst_gl->celldata);
1002
	dst_gl->cellsize = dst_gl->cellused = nx;
1003
1004
	/* Append as much as possible. */
1005
	grid_reflow_copy(dst_gl, ox, src_gl, 0, to_copy);
1006
1007
	/* If there is any left in the source, split it. */
1008
	if (src_gl->cellused > to_copy) {
1009
		dst_gl->flags |= GRID_LINE_WRAPPED;
1010
1011
		src_gl->cellused -= to_copy;
1012
		grid_reflow_split(dst, py, src_gl, new_x, to_copy);
1013
	}
1014
}
1015
1016
/* Split line data. */
1017
static void
1018
grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl,
1019
    u_int new_x, u_int offset)
1020
{
1021
	struct grid_line	*dst_gl = NULL;
1022
	u_int			 to_copy;
1023
1024
	/* Loop and copy sections of the source line. */
1025
	while (src_gl->cellused > 0) {
1026
		/* Create new line. */
1027
		if (*py >= dst->hsize + dst->sy)
1028
			grid_scroll_history(dst, 8);
1029
		dst_gl = &dst->linedata[*py];
1030
		(*py)++;
1031
1032
		/* How much should we copy? */
1033
		to_copy = new_x;
1034
		if (to_copy > src_gl->cellused)
1035
			to_copy = src_gl->cellused;
1036
1037
		/* Expand destination line. */
1038
		dst_gl->celldata = xreallocarray(NULL, to_copy,
1039
		    sizeof *dst_gl->celldata);
1040
		dst_gl->cellsize = dst_gl->cellused = to_copy;
1041
		dst_gl->flags |= GRID_LINE_WRAPPED;
1042
1043
		/* Copy the data. */
1044
		grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy);
1045
1046
		/* Move offset and reduce old line size. */
1047
		offset += to_copy;
1048
		src_gl->cellused -= to_copy;
1049
	}
1050
1051
	/* Last line is not wrapped. */
1052
	if (dst_gl != NULL)
1053
		dst_gl->flags &= ~GRID_LINE_WRAPPED;
1054
}
1055
1056
/* Move line data. */
1057
static void
1058
grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl)
1059
{
1060
	struct grid_line	*dst_gl;
1061
1062
	/* Create new line. */
1063
	if (*py >= dst->hsize + dst->sy)
1064
		grid_scroll_history(dst, 8);
1065
	dst_gl = &dst->linedata[*py];
1066
	(*py)++;
1067
1068
	/* Copy the old line. */
1069
	memcpy(dst_gl, src_gl, sizeof *dst_gl);
1070
	dst_gl->flags &= ~GRID_LINE_WRAPPED;
1071
1072
	/* Clear old line. */
1073
	src_gl->celldata = NULL;
1074
	src_gl->extddata = NULL;
1075
}
1076
1077
/*
1078
 * Reflow lines from src grid into dst grid of width new_x. Returns number of
1079
 * lines fewer in the visible area. The source grid is destroyed.
1080
 */
1081
u_int
1082
grid_reflow(struct grid *dst, struct grid *src, u_int new_x)
1083
{
1084
	u_int			 py, sy, line;
1085
	int			 previous_wrapped;
1086
	struct grid_line	*src_gl;
1087
1088
	py = 0;
1089
	sy = src->sy;
1090
1091
	previous_wrapped = 0;
1092
	for (line = 0; line < sy + src->hsize; line++) {
1093
		src_gl = src->linedata + line;
1094
		if (!previous_wrapped) {
1095
			/* Wasn't wrapped. If smaller, move to destination. */
1096
			if (src_gl->cellused <= new_x)
1097
				grid_reflow_move(dst, &py, src_gl);
1098
			else
1099
				grid_reflow_split(dst, &py, src_gl, new_x, 0);
1100
		} else {
1101
			/* Previous was wrapped. Try to join. */
1102
			grid_reflow_join(dst, &py, src_gl, new_x);
1103
		}
1104
		previous_wrapped = (src_gl->flags & GRID_LINE_WRAPPED);
1105
1106
		/* This is where we started scrolling. */
1107
		if (line == sy + src->hsize - src->hscrolled - 1)
1108
			dst->hscrolled = 0;
1109
	}
1110
1111
	grid_destroy(src);
1112
1113
	if (py > sy)
1114
		return (0);
1115
	return (sy - py);
1116
}