GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/tbl_term.c Lines: 335 350 95.7 %
Date: 2017-11-13 Branches: 293 345 84.9 %

Line Branch Exec Source
1
/*	$OpenBSD: tbl_term.c,v 1.45 2017/07/31 16:14:04 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4
 * Copyright (c) 2011,2012,2014,2015,2017 Ingo Schwarze <schwarze@openbsd.org>
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 USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
#include <sys/types.h>
19
20
#include <assert.h>
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include "mandoc.h"
26
#include "out.h"
27
#include "term.h"
28
29
#define	IS_HORIZ(cp)	((cp)->pos == TBL_CELL_HORIZ || \
30
			 (cp)->pos == TBL_CELL_DHORIZ)
31
32
static	size_t	term_tbl_len(size_t, void *);
33
static	size_t	term_tbl_strlen(const char *, void *);
34
static	size_t	term_tbl_sulen(const struct roffsu *, void *);
35
static	void	tbl_char(struct termp *, char, size_t);
36
static	void	tbl_data(struct termp *, const struct tbl_opts *,
37
			const struct tbl_cell *,
38
			const struct tbl_dat *,
39
			const struct roffcol *);
40
static	void	tbl_literal(struct termp *, const struct tbl_dat *,
41
			const struct roffcol *);
42
static	void	tbl_number(struct termp *, const struct tbl_opts *,
43
			const struct tbl_dat *,
44
			const struct roffcol *);
45
static	void	tbl_hrule(struct termp *, const struct tbl_span *, int);
46
static	void	tbl_word(struct termp *, const struct tbl_dat *);
47
48
49
static size_t
50
term_tbl_sulen(const struct roffsu *su, void *arg)
51
{
52
	int	 i;
53
54
36
	i = term_hen((const struct termp *)arg, su);
55
18
	return i > 0 ? i : 0;
56
}
57
58
static size_t
59
term_tbl_strlen(const char *p, void *arg)
60
{
61
33884
	return term_strlen((const struct termp *)arg, p);
62
}
63
64
static size_t
65
term_tbl_len(size_t sz, void *arg)
66
{
67
2736
	return term_len((const struct termp *)arg, sz);
68
}
69
70
void
71
term_tbl(struct termp *tp, const struct tbl_span *sp)
72
{
73
	const struct tbl_cell	*cp, *cpn, *cpp;
74
	const struct tbl_dat	*dp;
75
	static size_t		 offset;
76
	size_t			 coloff, tsz;
77
	int			 ic, horiz, spans, vert, more;
78
	char			 fc;
79
80
	/* Inhibit printing of spaces: we do padding ourselves. */
81
82
14334
	tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
83
84
	/*
85
	 * The first time we're invoked for a given table block,
86
	 * calculate the table widths and decimal positions.
87
	 */
88
89
7167
	if (tp->tbl.cols == NULL) {
90
2204
		tp->tbl.len = term_tbl_len;
91
2204
		tp->tbl.slen = term_tbl_strlen;
92
2204
		tp->tbl.sulen = term_tbl_sulen;
93
2204
		tp->tbl.arg = tp;
94
95
2204
		tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin);
96
97
		/* Tables leak .ta settings to subsequent text. */
98
99
2204
		term_tab_set(tp, NULL);
100
5046
		coloff = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
101
638
		    sp->opts->lvert;
102
16118
		for (ic = 0; ic < sp->opts->cols; ic++) {
103
5855
			coloff += tp->tbl.cols[ic].width;
104
5855
			term_tab_iset(coloff);
105
5855
			coloff += tp->tbl.cols[ic].spacing;
106
		}
107
108
		/* Center the table as a whole. */
109
110
2204
		offset = tp->tcol->offset;
111
2204
		if (sp->opts->opts & TBL_OPT_CENTRE) {
112
174
			tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
113
54
			    ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
114
132
			for (ic = 0; ic + 1 < sp->opts->cols; ic++)
115
12
				tsz += tp->tbl.cols[ic].width +
116
6
				    tp->tbl.cols[ic].spacing;
117
60
			if (sp->opts->cols)
118
60
				tsz += tp->tbl.cols[sp->opts->cols - 1].width;
119
60
			if (offset + tsz > tp->tcol->rmargin)
120
18
				tsz -= 1;
121
177
			tp->tcol->offset = offset + tp->tcol->rmargin > tsz ?
122
57
			    (offset + tp->tcol->rmargin - tsz) / 2 : 0;
123
60
		}
124
125
		/* Horizontal frame at the start of boxed tables. */
126
127
2204
		if (sp->opts->opts & TBL_OPT_DBOX)
128
732
			tbl_hrule(tp, sp, 3);
129
2204
		if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
130
1566
			tbl_hrule(tp, sp, 2);
131
	}
132
133
	/* Set up the columns. */
134
135
14334
	tp->flags |= TERMP_MULTICOL;
136
	horiz = 0;
137

14334
	switch (sp->pos) {
138
	case TBL_SPAN_HORIZ:
139
	case TBL_SPAN_DHORIZ:
140
		horiz = 1;
141
78
		term_setcol(tp, 1);
142
78
		break;
143
	case TBL_SPAN_DATA:
144
7089
		term_setcol(tp, sp->opts->cols + 2);
145
7089
		coloff = tp->tcol->offset;
146
147
		/* Set up a column for a left vertical frame. */
148
149

8982
		if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
150
1893
		    sp->opts->lvert)
151
5445
			coloff++;
152
7089
		tp->tcol->rmargin = coloff;
153
154
		/* Set up the data columns. */
155
156
7089
		dp = sp->first;
157
		spans = 0;
158
51612
		for (ic = 0; ic < sp->opts->cols; ic++) {
159
18717
			if (spans == 0) {
160
18672
				tp->tcol++;
161
18672
				tp->tcol->offset = coloff;
162
18672
			}
163
18717
			coloff += tp->tbl.cols[ic].width;
164
18717
			tp->tcol->rmargin = coloff;
165
18717
			if (ic + 1 < sp->opts->cols)
166
11628
				coloff += tp->tbl.cols[ic].spacing;
167
18717
			if (spans) {
168
45
				spans--;
169
45
				continue;
170
			}
171
18672
			if (dp == NULL)
172
				continue;
173
18186
			spans = dp->spans;
174

25266
			if (ic || sp->layout->first->pos != TBL_CELL_SPAN)
175
18186
				dp = dp->next;
176
		}
177
178
		/* Set up a column for a right vertical frame. */
179
180
7089
		tp->tcol++;
181
7089
		tp->tcol->offset = coloff + 1;
182
7089
		tp->tcol->rmargin = tp->maxrmargin;
183
184
		/* Spans may have reduced the number of columns. */
185
186
7089
		tp->lasttcol = tp->tcol - tp->tcols;
187
188
		/* Fill the buffers for all data columns. */
189
190
7089
		tp->tcol = tp->tcols;
191
7089
		cp = cpn = sp->layout->first;
192
7089
		dp = sp->first;
193
		spans = 0;
194
51612
		for (ic = 0; ic < sp->opts->cols; ic++) {
195
18717
			if (cpn != NULL) {
196
				cp = cpn;
197
18717
				cpn = cpn->next;
198
18717
			}
199
18717
			if (spans) {
200
45
				spans--;
201
45
				continue;
202
			}
203
18672
			tp->tcol++;
204
18672
			tp->col = 0;
205
18672
			tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
206
18672
			if (dp == NULL)
207
				continue;
208
18186
			spans = dp->spans;
209
18186
			if (cp->pos != TBL_CELL_SPAN)
210
18186
				dp = dp->next;
211
		}
212
		break;
213
	}
214
215
7167
	do {
216
		/* Print the vertical frame at the start of each row. */
217
218
7212
		tp->tcol = tp->tcols;
219
		fc = '\0';
220

13722
		if (sp->layout->vert ||
221

11479
		    (sp->next != NULL && sp->next->layout->vert &&
222
243
		     sp->next->pos == TBL_SPAN_DATA) ||
223

11056
		    (sp->prev != NULL && sp->prev->layout->vert &&
224

663
		     (horiz || (IS_HORIZ(sp->layout->first) &&
225

120
		       !IS_HORIZ(sp->prev->layout->first)))) ||
226
6510
		    sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))
227

21324
			fc = horiz || IS_HORIZ(sp->layout->first) ? '+' : '|';
228

1752
		else if (horiz && sp->opts->lvert)
229
3
			fc = '-';
230
7212
		if (fc != '\0') {
231
5472
			(*tp->advance)(tp, tp->tcols->offset);
232
5472
			(*tp->letter)(tp, fc);
233
5472
			tp->viscol = tp->tcol->offset + 1;
234
5472
		}
235
236
		/* Print the data cells. */
237
238
		more = 0;
239
7212
		if (horiz) {
240
78
			tbl_hrule(tp, sp, 0);
241
78
			term_flushln(tp);
242
78
		} else {
243
7134
			cp = sp->layout->first;
244
19174
			cpn = sp->next == NULL ? NULL :
245
4906
			    sp->next->layout->first;
246
19219
			cpp = sp->prev == NULL ? NULL :
247
4951
			    sp->prev->layout->first;
248
7134
			dp = sp->first;
249
			spans = 0;
250
52002
			for (ic = 0; ic < sp->opts->cols; ic++) {
251
252
				/*
253
				 * Figure out whether to print a
254
				 * vertical line after this cell
255
				 * and advance to next layout cell.
256
				 */
257
258
18867
				if (cp != NULL) {
259
18867
					vert = cp->vert;
260
18867
					switch (cp->pos) {
261
					case TBL_CELL_HORIZ:
262
						fc = '-';
263
1845
						break;
264
					case TBL_CELL_DHORIZ:
265
						fc = '=';
266
						break;
267
					default:
268
						fc = ' ';
269
17022
						break;
270
					}
271
				} else {
272
					vert = 0;
273
					fc = ' ';
274
				}
275
18867
				if (cpp != NULL) {
276

13711
					if (vert == 0 &&
277
					    cp != NULL &&
278

22556
					    ((IS_HORIZ(cp) &&
279

2148
					      !IS_HORIZ(cpp)) ||
280
10741
					     (cp->next != NULL &&
281
6339
					      cpp->next != NULL &&
282

12003
					      IS_HORIZ(cp->next) &&
283
1350
					      !IS_HORIZ(cpp->next))))
284
1749
						vert = cpp->vert;
285
13036
					cpp = cpp->next;
286
13036
				}
287

35847
				if (vert == 0 &&
288
16980
				    sp->opts->opts & TBL_OPT_ALLBOX)
289
5997
					vert = 1;
290
18867
				if (cpn != NULL) {
291
12904
					if (vert == 0)
292
7390
						vert = cpn->vert;
293
12904
					cpn = cpn->next;
294
12904
				}
295
18867
				if (cp != NULL)
296
18867
					cp = cp->next;
297
298
				/*
299
				 * Skip later cells in a span,
300
				 * figure out whether to start a span,
301
				 * and advance to next data cell.
302
				 */
303
304
18867
				if (spans) {
305
45
					spans--;
306
45
					continue;
307
				}
308
18822
				if (dp != NULL) {
309
18336
					spans = dp->spans;
310

32586
					if (ic || sp->layout->first->pos
311
7125
					    != TBL_CELL_SPAN)
312
18336
						dp = dp->next;
313
				}
314
315
				/*
316
				 * Print one line of text in the cell
317
				 * and remember whether there is more.
318
				 */
319
320
18822
				tp->tcol++;
321
18822
				if (tp->tcol->col < tp->tcol->lastcol)
322
18699
					term_flushln(tp);
323
18822
				if (tp->tcol->col < tp->tcol->lastcol)
324
45
					more = 1;
325
326
				/*
327
				 * Vertical frames between data cells,
328
				 * but not after the last column.
329
				 */
330
331

43572
				if (fc == ' ' && ((vert == 0 &&
332

20538
				     (cp == NULL || !IS_HORIZ(cp))) ||
333
7773
				    tp->tcol + 1 == tp->tcols + tp->lasttcol))
334
					continue;
335
336
6936
				if (tp->viscol < tp->tcol->rmargin) {
337
276
					(*tp->advance)(tp, tp->tcol->rmargin
338
138
					   - tp->viscol);
339
138
					tp->viscol = tp->tcol->rmargin;
340
138
				}
341
41733
				while (tp->viscol < tp->tcol->rmargin +
342
13911
				    tp->tbl.cols[ic].spacing / 2) {
343
6975
					(*tp->letter)(tp, fc);
344
6975
					tp->viscol++;
345
				}
346
347
6936
				if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
348
					continue;
349
350
6465
				if (fc == ' ' && cp != NULL) {
351
6201
					switch (cp->pos) {
352
					case TBL_CELL_HORIZ:
353
						fc = '-';
354
1110
						break;
355
					case TBL_CELL_DHORIZ:
356
						fc = '=';
357
						break;
358
					default:
359
						break;
360
					}
361
				}
362
6465
				if (tp->tbl.cols[ic].spacing) {
363
12930
					(*tp->letter)(tp, fc == ' ' ? '|' :
364
2484
					    vert ? '+' : fc);
365
6465
					tp->viscol++;
366
6465
				}
367
368
6465
				if (fc != ' ') {
369

4968
					if (cp != NULL &&
370
2484
					    cp->pos == TBL_CELL_HORIZ)
371
1371
						fc = '-';
372

2226
					else if (cp != NULL &&
373
1113
					    cp->pos == TBL_CELL_DHORIZ)
374
						fc = '=';
375
					else
376
						fc = ' ';
377
				}
378

11919
				if (tp->tbl.cols[ic].spacing > 2 &&
379
11901
				    (vert > 1 || fc != ' ')) {
380
4200
					(*tp->letter)(tp, fc == ' ' ? '|' :
381
1371
					    vert > 1 ? '+' : fc);
382
2100
					tp->viscol++;
383
2100
				}
384
			}
385
		}
386
387
		/* Print the vertical frame at the end of each row. */
388
389
		fc = '\0';
390

13734
		if ((sp->layout->last->vert &&
391
387
		     sp->layout->last->col + 1 == sp->opts->cols) ||
392
6825
		    (sp->next != NULL &&
393
4672
		     sp->next->layout->last->vert &&
394
240
		     sp->next->layout->last->col + 1 == sp->opts->cols) ||
395
6585
		    (sp->prev != NULL &&
396
4492
		     sp->prev->layout->last->vert &&
397
243
		     sp->prev->layout->last->col + 1 == sp->opts->cols &&
398

663
		     (horiz || (IS_HORIZ(sp->layout->last) &&
399

120
		      !IS_HORIZ(sp->prev->layout->last)))) ||
400
6522
		    (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)))
401

21282
			fc = horiz || IS_HORIZ(sp->layout->last) ? '+' : '|';
402

1767
		else if (horiz && sp->opts->rvert)
403
3
			fc = '-';
404
7212
		if (fc != '\0') {
405


21633
			if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||
406
414
			    sp->layout->last->col + 1 < sp->opts->cols)) {
407
4977
				tp->tcol++;
408
9954
				(*tp->advance)(tp,
409
14925
				    tp->tcol->offset > tp->viscol ?
410
4971
				    tp->tcol->offset - tp->viscol : 1);
411
4977
			}
412
5460
			(*tp->letter)(tp, fc);
413
5460
		}
414
7212
		(*tp->endline)(tp);
415
7212
		tp->viscol = 0;
416
7212
	} while (more);
417
418
	/*
419
	 * Clean up after this row.  If it is the last line
420
	 * of the table, print the box line and clean up
421
	 * column data; otherwise, print the allbox line.
422
	 */
423
424
7167
	term_setcol(tp, 1);
425
7167
	tp->flags &= ~TERMP_MULTICOL;
426
7167
	tp->tcol->rmargin = tp->maxrmargin;
427
7167
	if (sp->next == NULL) {
428
2204
		if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) {
429
1566
			tbl_hrule(tp, sp, 2);
430
1566
			tp->skipvsp = 1;
431
1566
		}
432
2204
		if (sp->opts->opts & TBL_OPT_DBOX) {
433
732
			tbl_hrule(tp, sp, 3);
434
732
			tp->skipvsp = 2;
435
732
		}
436
2204
		assert(tp->tbl.cols);
437
2204
		free(tp->tbl.cols);
438
2204
		tp->tbl.cols = NULL;
439
2204
		tp->tcol->offset = offset;
440

12070
	} else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX &&
441

3582
	    (sp->next == NULL || sp->next->pos == TBL_SPAN_DATA ||
442
6
	     sp->next->next != NULL))
443
1788
		tbl_hrule(tp, sp, 1);
444
445
7167
	tp->flags &= ~TERMP_NONOSPACE;
446
7167
}
447
448
/*
449
 * Kinds of horizontal rulers:
450
 * 0: inside the table (single or double line with crossings)
451
 * 1: inside the table (single or double line with crossings and ends)
452
 * 2: inner frame (single line with crossings and ends)
453
 * 3: outer frame (single line without crossings with ends)
454
 */
455
static void
456
tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind)
457
{
458
	const struct tbl_cell *cp, *cpn, *cpp;
459
	const struct roffcol *col;
460
	int	 vert;
461
	char	 line, cross;
462
463
21252
	line = (kind < 2 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-';
464
6462
	cross = (kind < 3) ? '+' : '-';
465
466
6462
	if (kind)
467
6384
		term_word(tp, "+");
468
6462
	cp = sp->layout->first;
469

13050
	cpp = kind || sp->prev == NULL ? NULL : sp->prev->layout->first;
470
6462
	if (cpp == cp)
471
		cpp = NULL;
472

16644
	cpn = kind > 1 || sp->next == NULL ? NULL : sp->next->layout->first;
473
6462
	if (cpn == cp)
474
		cpn = NULL;
475
16347
	for (;;) {
476
16347
		col = tp->tbl.cols + cp->col;
477
16347
		tbl_char(tp, line, col->width + col->spacing / 2);
478
16347
		vert = cp->vert;
479
16347
		if ((cp = cp->next) == NULL)
480
			 break;
481
9885
		if (cpp != NULL) {
482
12
			if (vert < cpp->vert)
483
6
				vert = cpp->vert;
484
12
			cpp = cpp->next;
485
12
		}
486
9885
		if (cpn != NULL) {
487
2688
			if (vert < cpn->vert)
488
348
				vert = cpn->vert;
489
2688
			cpn = cpn->next;
490
2688
		}
491
9885
		if (sp->opts->opts & TBL_OPT_ALLBOX && !vert)
492
5391
			vert = 1;
493
9885
		if (col->spacing)
494
9879
			tbl_char(tp, vert ? cross : line, 1);
495
9885
		if (col->spacing > 2)
496
9867
			tbl_char(tp, vert > 1 ? cross : line, 1);
497
9885
		if (col->spacing > 4)
498
18
			tbl_char(tp, line, (col->spacing - 3) / 2);
499
	}
500
6462
	if (kind) {
501
6384
		term_word(tp, "+");
502
6384
		term_flushln(tp);
503
6384
	}
504
6462
}
505
506
static void
507
tbl_data(struct termp *tp, const struct tbl_opts *opts,
508
    const struct tbl_cell *cp, const struct tbl_dat *dp,
509
    const struct roffcol *col)
510
{
511
37344
	switch (cp->pos) {
512
	case TBL_CELL_HORIZ:
513
1845
		tbl_char(tp, '-', col->width);
514
1845
		return;
515
	case TBL_CELL_DHORIZ:
516
		tbl_char(tp, '=', col->width);
517
		return;
518
	default:
519
		break;
520
	}
521
522
16827
	if (dp == NULL)
523
		return;
524
525

16818
	switch (dp->pos) {
526
	case TBL_DATA_NONE:
527
		return;
528
	case TBL_DATA_HORIZ:
529
	case TBL_DATA_NHORIZ:
530
		tbl_char(tp, '-', col->width);
531
		return;
532
	case TBL_DATA_NDHORIZ:
533
	case TBL_DATA_DHORIZ:
534
		tbl_char(tp, '=', col->width);
535
		return;
536
	default:
537
		break;
538
	}
539
540


33627
	switch (cp->pos) {
541
	case TBL_CELL_LONG:
542
	case TBL_CELL_CENTRE:
543
	case TBL_CELL_LEFT:
544
	case TBL_CELL_RIGHT:
545
16770
		tbl_literal(tp, dp, col);
546
16770
		break;
547
	case TBL_CELL_NUMBER:
548
39
		tbl_number(tp, opts, dp, col);
549
39
		break;
550
	case TBL_CELL_DOWN:
551
	case TBL_CELL_SPAN:
552
		break;
553
	default:
554
		abort();
555
	}
556
18672
}
557
558
static void
559
tbl_char(struct termp *tp, char c, size_t len)
560
{
561
	size_t		i, sz;
562
143040
	char		cp[2];
563
564
71520
	cp[0] = c;
565
71520
	cp[1] = '\0';
566
567
71520
	sz = term_strlen(tp, cp);
568
569
301632
	for (i = 0; i < len; i += sz)
570
79296
		term_word(tp, cp);
571
71520
}
572
573
static void
574
tbl_literal(struct termp *tp, const struct tbl_dat *dp,
575
		const struct roffcol *col)
576
{
577
	size_t		 len, padl, padr, width;
578
	int		 ic, spans;
579
580
33540
	assert(dp->string);
581
16770
	len = term_strlen(tp, dp->string);
582
16770
	width = col->width;
583
16770
	ic = dp->layout->col;
584
16770
	spans = dp->spans;
585
33630
	while (spans--)
586
45
		width += tp->tbl.cols[++ic].width + 3;
587
588
35062
	padr = width > len ? width - len : 0;
589
	padl = 0;
590
591

17037
	switch (dp->layout->pos) {
592
	case TBL_CELL_LONG:
593
		padl = term_len(tp, 1);
594
		padr = padr > padl ? padr - padl : 0;
595
		break;
596
	case TBL_CELL_CENTRE:
597
96
		if (2 > padr)
598
			break;
599
48
		padl = padr / 2;
600
48
		padr -= padl;
601
48
		break;
602
	case TBL_CELL_RIGHT:
603
		padl = padr;
604
		padr = 0;
605
171
		break;
606
	default:
607
		break;
608
	}
609
610
16770
	tbl_char(tp, ASCII_NBRSP, padl);
611
16770
	tbl_word(tp, dp);
612
16770
	tbl_char(tp, ASCII_NBRSP, padr);
613
16770
}
614
615
static void
616
tbl_number(struct termp *tp, const struct tbl_opts *opts,
617
		const struct tbl_dat *dp,
618
		const struct roffcol *col)
619
{
620
	char		*cp;
621
78
	char		 buf[2];
622
	size_t		 sz, psz, ssz, d, padl;
623
	int		 i;
624
625
	/*
626
	 * See calc_data_number().  Left-pad by taking the offset of our
627
	 * and the maximum decimal; right-pad by the remaining amount.
628
	 */
629
630
39
	assert(dp->string);
631
632
39
	sz = term_strlen(tp, dp->string);
633
634
39
	buf[0] = opts->decimal;
635
39
	buf[1] = '\0';
636
637
39
	psz = term_strlen(tp, buf);
638
639
39
	if ((cp = strrchr(dp->string, opts->decimal)) != NULL) {
640
228
		for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
641
75
			buf[0] = dp->string[i];
642
75
			ssz += term_strlen(tp, buf);
643
		}
644
39
		d = ssz + psz;
645
39
	} else
646
		d = sz + psz;
647
648

57
	if (col->decimal > d && col->width > sz) {
649
12
		padl = col->decimal - d;
650
12
		if (padl + sz > col->width)
651
			padl = col->width - sz;
652
12
		tbl_char(tp, ASCII_NBRSP, padl);
653
12
	} else
654
		padl = 0;
655
39
	tbl_word(tp, dp);
656
39
	if (col->width > sz + padl)
657
12
		tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
658
39
}
659
660
static void
661
tbl_word(struct termp *tp, const struct tbl_dat *dp)
662
{
663
	int		 prev_font;
664
665
33618
	prev_font = tp->fonti;
666
16809
	if (dp->layout->flags & TBL_CELL_BOLD)
667
27
		term_fontpush(tp, TERMFONT_BOLD);
668
16782
	else if (dp->layout->flags & TBL_CELL_ITALIC)
669
18
		term_fontpush(tp, TERMFONT_UNDER);
670
671
16809
	term_word(tp, dp->string);
672
673
16809
	term_fontpopq(tp, prev_font);
674
16809
}