xref: /titanic_44/usr/src/cmd/mandoc/tbl_term.c (revision 698f87a48e2e945bfe5493ce168e0d0ae1cedd5c)
1*698f87a4SGarrett D'Amore /*	$Id: tbl_term.c,v 1.25 2013/05/31 21:37:17 schwarze Exp $ */
295c635efSGarrett D'Amore /*
395c635efSGarrett D'Amore  * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*698f87a4SGarrett D'Amore  * Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
1095c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore  */
1895c635efSGarrett D'Amore #ifdef HAVE_CONFIG_H
1995c635efSGarrett D'Amore #include "config.h"
2095c635efSGarrett D'Amore #endif
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <assert.h>
2395c635efSGarrett D'Amore #include <stdio.h>
2495c635efSGarrett D'Amore #include <stdlib.h>
2595c635efSGarrett D'Amore #include <string.h>
2695c635efSGarrett D'Amore 
2795c635efSGarrett D'Amore #include "mandoc.h"
2895c635efSGarrett D'Amore #include "out.h"
2995c635efSGarrett D'Amore #include "term.h"
3095c635efSGarrett D'Amore 
3195c635efSGarrett D'Amore static	size_t	term_tbl_len(size_t, void *);
3295c635efSGarrett D'Amore static	size_t	term_tbl_strlen(const char *, void *);
3395c635efSGarrett D'Amore static	void	tbl_char(struct termp *, char, size_t);
34*698f87a4SGarrett D'Amore static	void	tbl_data(struct termp *, const struct tbl_opts *,
3595c635efSGarrett D'Amore 			const struct tbl_dat *,
3695c635efSGarrett D'Amore 			const struct roffcol *);
3795c635efSGarrett D'Amore static	size_t	tbl_rulewidth(struct termp *, const struct tbl_head *);
3895c635efSGarrett D'Amore static	void	tbl_hframe(struct termp *, const struct tbl_span *, int);
3995c635efSGarrett D'Amore static	void	tbl_literal(struct termp *, const struct tbl_dat *,
4095c635efSGarrett D'Amore 			const struct roffcol *);
41*698f87a4SGarrett D'Amore static	void	tbl_number(struct termp *, const struct tbl_opts *,
4295c635efSGarrett D'Amore 			const struct tbl_dat *,
4395c635efSGarrett D'Amore 			const struct roffcol *);
4495c635efSGarrett D'Amore static	void	tbl_hrule(struct termp *, const struct tbl_span *);
4595c635efSGarrett D'Amore static	void	tbl_vrule(struct termp *, const struct tbl_head *);
4695c635efSGarrett D'Amore 
4795c635efSGarrett D'Amore 
4895c635efSGarrett D'Amore static size_t
term_tbl_strlen(const char * p,void * arg)4995c635efSGarrett D'Amore term_tbl_strlen(const char *p, void *arg)
5095c635efSGarrett D'Amore {
5195c635efSGarrett D'Amore 
5295c635efSGarrett D'Amore 	return(term_strlen((const struct termp *)arg, p));
5395c635efSGarrett D'Amore }
5495c635efSGarrett D'Amore 
5595c635efSGarrett D'Amore static size_t
term_tbl_len(size_t sz,void * arg)5695c635efSGarrett D'Amore term_tbl_len(size_t sz, void *arg)
5795c635efSGarrett D'Amore {
5895c635efSGarrett D'Amore 
5995c635efSGarrett D'Amore 	return(term_len((const struct termp *)arg, sz));
6095c635efSGarrett D'Amore }
6195c635efSGarrett D'Amore 
6295c635efSGarrett D'Amore void
term_tbl(struct termp * tp,const struct tbl_span * sp)6395c635efSGarrett D'Amore term_tbl(struct termp *tp, const struct tbl_span *sp)
6495c635efSGarrett D'Amore {
6595c635efSGarrett D'Amore 	const struct tbl_head	*hp;
6695c635efSGarrett D'Amore 	const struct tbl_dat	*dp;
6795c635efSGarrett D'Amore 	struct roffcol		*col;
6895c635efSGarrett D'Amore 	int			 spans;
6995c635efSGarrett D'Amore 	size_t		   	 rmargin, maxrmargin;
7095c635efSGarrett D'Amore 
7195c635efSGarrett D'Amore 	rmargin = tp->rmargin;
7295c635efSGarrett D'Amore 	maxrmargin = tp->maxrmargin;
7395c635efSGarrett D'Amore 
7495c635efSGarrett D'Amore 	tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
7595c635efSGarrett D'Amore 
7695c635efSGarrett D'Amore 	/* Inhibit printing of spaces: we do padding ourselves. */
7795c635efSGarrett D'Amore 
7895c635efSGarrett D'Amore 	tp->flags |= TERMP_NONOSPACE;
7995c635efSGarrett D'Amore 	tp->flags |= TERMP_NOSPACE;
8095c635efSGarrett D'Amore 
8195c635efSGarrett D'Amore 	/*
8295c635efSGarrett D'Amore 	 * The first time we're invoked for a given table block,
8395c635efSGarrett D'Amore 	 * calculate the table widths and decimal positions.
8495c635efSGarrett D'Amore 	 */
8595c635efSGarrett D'Amore 
8695c635efSGarrett D'Amore 	if (TBL_SPAN_FIRST & sp->flags) {
8795c635efSGarrett D'Amore 		term_flushln(tp);
8895c635efSGarrett D'Amore 
8995c635efSGarrett D'Amore 		tp->tbl.len = term_tbl_len;
9095c635efSGarrett D'Amore 		tp->tbl.slen = term_tbl_strlen;
9195c635efSGarrett D'Amore 		tp->tbl.arg = tp;
9295c635efSGarrett D'Amore 
9395c635efSGarrett D'Amore 		tblcalc(&tp->tbl, sp);
9495c635efSGarrett D'Amore 	}
9595c635efSGarrett D'Amore 
9695c635efSGarrett D'Amore 	/* Horizontal frame at the start of boxed tables. */
9795c635efSGarrett D'Amore 
9895c635efSGarrett D'Amore 	if (TBL_SPAN_FIRST & sp->flags) {
99*698f87a4SGarrett D'Amore 		if (TBL_OPT_DBOX & sp->opts->opts)
10095c635efSGarrett D'Amore 			tbl_hframe(tp, sp, 1);
101*698f87a4SGarrett D'Amore 		if (TBL_OPT_DBOX & sp->opts->opts ||
102*698f87a4SGarrett D'Amore 		    TBL_OPT_BOX  & sp->opts->opts)
10395c635efSGarrett D'Amore 			tbl_hframe(tp, sp, 0);
10495c635efSGarrett D'Amore 	}
10595c635efSGarrett D'Amore 
10695c635efSGarrett D'Amore 	/* Vertical frame at the start of each row. */
10795c635efSGarrett D'Amore 
108*698f87a4SGarrett D'Amore 	if (TBL_OPT_BOX & sp->opts->opts || TBL_OPT_DBOX & sp->opts->opts)
10995c635efSGarrett D'Amore 		term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
11095c635efSGarrett D'Amore 			TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
11195c635efSGarrett D'Amore 
11295c635efSGarrett D'Amore 	/*
11395c635efSGarrett D'Amore 	 * Now print the actual data itself depending on the span type.
11495c635efSGarrett D'Amore 	 * Spanner spans get a horizontal rule; data spanners have their
11595c635efSGarrett D'Amore 	 * data printed by matching data to header.
11695c635efSGarrett D'Amore 	 */
11795c635efSGarrett D'Amore 
11895c635efSGarrett D'Amore 	switch (sp->pos) {
11995c635efSGarrett D'Amore 	case (TBL_SPAN_HORIZ):
12095c635efSGarrett D'Amore 		/* FALLTHROUGH */
12195c635efSGarrett D'Amore 	case (TBL_SPAN_DHORIZ):
12295c635efSGarrett D'Amore 		tbl_hrule(tp, sp);
12395c635efSGarrett D'Amore 		break;
12495c635efSGarrett D'Amore 	case (TBL_SPAN_DATA):
12595c635efSGarrett D'Amore 		/* Iterate over template headers. */
12695c635efSGarrett D'Amore 		dp = sp->first;
12795c635efSGarrett D'Amore 		spans = 0;
12895c635efSGarrett D'Amore 		for (hp = sp->head; hp; hp = hp->next) {
129*698f87a4SGarrett D'Amore 
13095c635efSGarrett D'Amore 			/*
13195c635efSGarrett D'Amore 			 * If the current data header is invoked during
13295c635efSGarrett D'Amore 			 * a spanner ("spans" > 0), don't emit anything
13395c635efSGarrett D'Amore 			 * at all.
13495c635efSGarrett D'Amore 			 */
13595c635efSGarrett D'Amore 
13695c635efSGarrett D'Amore 			if (--spans >= 0)
13795c635efSGarrett D'Amore 				continue;
13895c635efSGarrett D'Amore 
139*698f87a4SGarrett D'Amore 			/* Separate columns. */
14095c635efSGarrett D'Amore 
141*698f87a4SGarrett D'Amore 			if (NULL != hp->prev)
142*698f87a4SGarrett D'Amore 				tbl_vrule(tp, hp);
14395c635efSGarrett D'Amore 
14495c635efSGarrett D'Amore 			col = &tp->tbl.cols[hp->ident];
145*698f87a4SGarrett D'Amore 			tbl_data(tp, sp->opts, dp, col);
14695c635efSGarrett D'Amore 
14795c635efSGarrett D'Amore 			/*
14895c635efSGarrett D'Amore 			 * Go to the next data cell and assign the
14995c635efSGarrett D'Amore 			 * number of subsequent spans, if applicable.
15095c635efSGarrett D'Amore 			 */
15195c635efSGarrett D'Amore 
15295c635efSGarrett D'Amore 			if (dp) {
15395c635efSGarrett D'Amore 				spans = dp->spans;
15495c635efSGarrett D'Amore 				dp = dp->next;
15595c635efSGarrett D'Amore 			}
15695c635efSGarrett D'Amore 		}
15795c635efSGarrett D'Amore 		break;
15895c635efSGarrett D'Amore 	}
15995c635efSGarrett D'Amore 
16095c635efSGarrett D'Amore 	/* Vertical frame at the end of each row. */
16195c635efSGarrett D'Amore 
162*698f87a4SGarrett D'Amore 	if (TBL_OPT_BOX & sp->opts->opts || TBL_OPT_DBOX & sp->opts->opts)
16395c635efSGarrett D'Amore 		term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
16495c635efSGarrett D'Amore 			TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
16595c635efSGarrett D'Amore 	term_flushln(tp);
16695c635efSGarrett D'Amore 
16795c635efSGarrett D'Amore 	/*
16895c635efSGarrett D'Amore 	 * If we're the last row, clean up after ourselves: clear the
16995c635efSGarrett D'Amore 	 * existing table configuration and set it to NULL.
17095c635efSGarrett D'Amore 	 */
17195c635efSGarrett D'Amore 
17295c635efSGarrett D'Amore 	if (TBL_SPAN_LAST & sp->flags) {
173*698f87a4SGarrett D'Amore 		if (TBL_OPT_DBOX & sp->opts->opts ||
174*698f87a4SGarrett D'Amore 		    TBL_OPT_BOX  & sp->opts->opts) {
17595c635efSGarrett D'Amore 			tbl_hframe(tp, sp, 0);
176*698f87a4SGarrett D'Amore 			tp->skipvsp = 1;
177*698f87a4SGarrett D'Amore 		}
178*698f87a4SGarrett D'Amore 		if (TBL_OPT_DBOX & sp->opts->opts) {
17995c635efSGarrett D'Amore 			tbl_hframe(tp, sp, 1);
180*698f87a4SGarrett D'Amore 			tp->skipvsp = 2;
181*698f87a4SGarrett D'Amore 		}
18295c635efSGarrett D'Amore 		assert(tp->tbl.cols);
18395c635efSGarrett D'Amore 		free(tp->tbl.cols);
18495c635efSGarrett D'Amore 		tp->tbl.cols = NULL;
18595c635efSGarrett D'Amore 	}
18695c635efSGarrett D'Amore 
18795c635efSGarrett D'Amore 	tp->flags &= ~TERMP_NONOSPACE;
18895c635efSGarrett D'Amore 	tp->rmargin = rmargin;
18995c635efSGarrett D'Amore 	tp->maxrmargin = maxrmargin;
19095c635efSGarrett D'Amore 
19195c635efSGarrett D'Amore }
19295c635efSGarrett D'Amore 
19395c635efSGarrett D'Amore /*
19495c635efSGarrett D'Amore  * Horizontal rules extend across the entire table.
19595c635efSGarrett D'Amore  * Calculate the width by iterating over columns.
19695c635efSGarrett D'Amore  */
19795c635efSGarrett D'Amore static size_t
tbl_rulewidth(struct termp * tp,const struct tbl_head * hp)19895c635efSGarrett D'Amore tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
19995c635efSGarrett D'Amore {
20095c635efSGarrett D'Amore 	size_t		 width;
20195c635efSGarrett D'Amore 
20295c635efSGarrett D'Amore 	width = tp->tbl.cols[hp->ident].width;
203*698f87a4SGarrett D'Amore 
20495c635efSGarrett D'Amore 	/* Account for leading blanks. */
205*698f87a4SGarrett D'Amore 	if (hp->prev)
206*698f87a4SGarrett D'Amore 		width += 2 - hp->vert;
207*698f87a4SGarrett D'Amore 
208*698f87a4SGarrett D'Amore 	/* Account for trailing blank. */
20995c635efSGarrett D'Amore 	width++;
210*698f87a4SGarrett D'Amore 
21195c635efSGarrett D'Amore 	return(width);
21295c635efSGarrett D'Amore }
21395c635efSGarrett D'Amore 
21495c635efSGarrett D'Amore /*
21595c635efSGarrett D'Amore  * Rules inside the table can be single or double
21695c635efSGarrett D'Amore  * and have crossings with vertical rules marked with pluses.
21795c635efSGarrett D'Amore  */
21895c635efSGarrett D'Amore static void
tbl_hrule(struct termp * tp,const struct tbl_span * sp)21995c635efSGarrett D'Amore tbl_hrule(struct termp *tp, const struct tbl_span *sp)
22095c635efSGarrett D'Amore {
22195c635efSGarrett D'Amore 	const struct tbl_head *hp;
22295c635efSGarrett D'Amore 	char		 c;
22395c635efSGarrett D'Amore 
22495c635efSGarrett D'Amore 	c = '-';
22595c635efSGarrett D'Amore 	if (TBL_SPAN_DHORIZ == sp->pos)
22695c635efSGarrett D'Amore 		c = '=';
22795c635efSGarrett D'Amore 
228*698f87a4SGarrett D'Amore 	for (hp = sp->head; hp; hp = hp->next) {
229*698f87a4SGarrett D'Amore 		if (hp->prev && hp->vert)
230*698f87a4SGarrett D'Amore 			tbl_char(tp, '+', hp->vert);
231*698f87a4SGarrett D'Amore 		tbl_char(tp, c, tbl_rulewidth(tp, hp));
232*698f87a4SGarrett D'Amore 	}
23395c635efSGarrett D'Amore }
23495c635efSGarrett D'Amore 
23595c635efSGarrett D'Amore /*
23695c635efSGarrett D'Amore  * Rules above and below the table are always single
23795c635efSGarrett D'Amore  * and have an additional plus at the beginning and end.
23895c635efSGarrett D'Amore  * For double frames, this function is called twice,
23995c635efSGarrett D'Amore  * and the outer one does not have crossings.
24095c635efSGarrett D'Amore  */
24195c635efSGarrett D'Amore static void
tbl_hframe(struct termp * tp,const struct tbl_span * sp,int outer)24295c635efSGarrett D'Amore tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
24395c635efSGarrett D'Amore {
24495c635efSGarrett D'Amore 	const struct tbl_head *hp;
24595c635efSGarrett D'Amore 
24695c635efSGarrett D'Amore 	term_word(tp, "+");
247*698f87a4SGarrett D'Amore 	for (hp = sp->head; hp; hp = hp->next) {
248*698f87a4SGarrett D'Amore 		if (hp->prev && hp->vert)
249*698f87a4SGarrett D'Amore 			tbl_char(tp, (outer ? '-' : '+'), hp->vert);
250*698f87a4SGarrett D'Amore 		tbl_char(tp, '-', tbl_rulewidth(tp, hp));
251*698f87a4SGarrett D'Amore 	}
25295c635efSGarrett D'Amore 	term_word(tp, "+");
25395c635efSGarrett D'Amore 	term_flushln(tp);
25495c635efSGarrett D'Amore }
25595c635efSGarrett D'Amore 
25695c635efSGarrett D'Amore static void
tbl_data(struct termp * tp,const struct tbl_opts * opts,const struct tbl_dat * dp,const struct roffcol * col)257*698f87a4SGarrett D'Amore tbl_data(struct termp *tp, const struct tbl_opts *opts,
25895c635efSGarrett D'Amore 		const struct tbl_dat *dp,
25995c635efSGarrett D'Amore 		const struct roffcol *col)
26095c635efSGarrett D'Amore {
26195c635efSGarrett D'Amore 
26295c635efSGarrett D'Amore 	if (NULL == dp) {
26395c635efSGarrett D'Amore 		tbl_char(tp, ASCII_NBRSP, col->width);
26495c635efSGarrett D'Amore 		return;
26595c635efSGarrett D'Amore 	}
26695c635efSGarrett D'Amore 	assert(dp->layout);
26795c635efSGarrett D'Amore 
26895c635efSGarrett D'Amore 	switch (dp->pos) {
26995c635efSGarrett D'Amore 	case (TBL_DATA_NONE):
27095c635efSGarrett D'Amore 		tbl_char(tp, ASCII_NBRSP, col->width);
27195c635efSGarrett D'Amore 		return;
27295c635efSGarrett D'Amore 	case (TBL_DATA_HORIZ):
27395c635efSGarrett D'Amore 		/* FALLTHROUGH */
27495c635efSGarrett D'Amore 	case (TBL_DATA_NHORIZ):
27595c635efSGarrett D'Amore 		tbl_char(tp, '-', col->width);
27695c635efSGarrett D'Amore 		return;
27795c635efSGarrett D'Amore 	case (TBL_DATA_NDHORIZ):
27895c635efSGarrett D'Amore 		/* FALLTHROUGH */
27995c635efSGarrett D'Amore 	case (TBL_DATA_DHORIZ):
28095c635efSGarrett D'Amore 		tbl_char(tp, '=', col->width);
28195c635efSGarrett D'Amore 		return;
28295c635efSGarrett D'Amore 	default:
28395c635efSGarrett D'Amore 		break;
28495c635efSGarrett D'Amore 	}
28595c635efSGarrett D'Amore 
28695c635efSGarrett D'Amore 	switch (dp->layout->pos) {
28795c635efSGarrett D'Amore 	case (TBL_CELL_HORIZ):
28895c635efSGarrett D'Amore 		tbl_char(tp, '-', col->width);
28995c635efSGarrett D'Amore 		break;
29095c635efSGarrett D'Amore 	case (TBL_CELL_DHORIZ):
29195c635efSGarrett D'Amore 		tbl_char(tp, '=', col->width);
29295c635efSGarrett D'Amore 		break;
29395c635efSGarrett D'Amore 	case (TBL_CELL_LONG):
29495c635efSGarrett D'Amore 		/* FALLTHROUGH */
29595c635efSGarrett D'Amore 	case (TBL_CELL_CENTRE):
29695c635efSGarrett D'Amore 		/* FALLTHROUGH */
29795c635efSGarrett D'Amore 	case (TBL_CELL_LEFT):
29895c635efSGarrett D'Amore 		/* FALLTHROUGH */
29995c635efSGarrett D'Amore 	case (TBL_CELL_RIGHT):
30095c635efSGarrett D'Amore 		tbl_literal(tp, dp, col);
30195c635efSGarrett D'Amore 		break;
30295c635efSGarrett D'Amore 	case (TBL_CELL_NUMBER):
303*698f87a4SGarrett D'Amore 		tbl_number(tp, opts, dp, col);
30495c635efSGarrett D'Amore 		break;
30595c635efSGarrett D'Amore 	case (TBL_CELL_DOWN):
30695c635efSGarrett D'Amore 		tbl_char(tp, ASCII_NBRSP, col->width);
30795c635efSGarrett D'Amore 		break;
30895c635efSGarrett D'Amore 	default:
30995c635efSGarrett D'Amore 		abort();
31095c635efSGarrett D'Amore 		/* NOTREACHED */
31195c635efSGarrett D'Amore 	}
31295c635efSGarrett D'Amore }
31395c635efSGarrett D'Amore 
31495c635efSGarrett D'Amore static void
tbl_vrule(struct termp * tp,const struct tbl_head * hp)31595c635efSGarrett D'Amore tbl_vrule(struct termp *tp, const struct tbl_head *hp)
31695c635efSGarrett D'Amore {
31795c635efSGarrett D'Amore 
318*698f87a4SGarrett D'Amore 	tbl_char(tp, ASCII_NBRSP, 1);
319*698f87a4SGarrett D'Amore 	if (0 < hp->vert)
320*698f87a4SGarrett D'Amore 		tbl_char(tp, '|', hp->vert);
321*698f87a4SGarrett D'Amore 	if (2 > hp->vert)
322*698f87a4SGarrett D'Amore 		tbl_char(tp, ASCII_NBRSP, 2 - hp->vert);
32395c635efSGarrett D'Amore }
32495c635efSGarrett D'Amore 
32595c635efSGarrett D'Amore static void
tbl_char(struct termp * tp,char c,size_t len)32695c635efSGarrett D'Amore tbl_char(struct termp *tp, char c, size_t len)
32795c635efSGarrett D'Amore {
32895c635efSGarrett D'Amore 	size_t		i, sz;
32995c635efSGarrett D'Amore 	char		cp[2];
33095c635efSGarrett D'Amore 
33195c635efSGarrett D'Amore 	cp[0] = c;
33295c635efSGarrett D'Amore 	cp[1] = '\0';
33395c635efSGarrett D'Amore 
33495c635efSGarrett D'Amore 	sz = term_strlen(tp, cp);
33595c635efSGarrett D'Amore 
33695c635efSGarrett D'Amore 	for (i = 0; i < len; i += sz)
33795c635efSGarrett D'Amore 		term_word(tp, cp);
33895c635efSGarrett D'Amore }
33995c635efSGarrett D'Amore 
34095c635efSGarrett D'Amore static void
tbl_literal(struct termp * tp,const struct tbl_dat * dp,const struct roffcol * col)34195c635efSGarrett D'Amore tbl_literal(struct termp *tp, const struct tbl_dat *dp,
34295c635efSGarrett D'Amore 		const struct roffcol *col)
34395c635efSGarrett D'Amore {
344*698f87a4SGarrett D'Amore 	struct tbl_head		*hp;
345*698f87a4SGarrett D'Amore 	size_t			 width, len, padl, padr;
346*698f87a4SGarrett D'Amore 	int			 spans;
34795c635efSGarrett D'Amore 
34895c635efSGarrett D'Amore 	assert(dp->string);
34995c635efSGarrett D'Amore 	len = term_strlen(tp, dp->string);
350*698f87a4SGarrett D'Amore 
351*698f87a4SGarrett D'Amore 	hp = dp->layout->head->next;
352*698f87a4SGarrett D'Amore 	width = col->width;
353*698f87a4SGarrett D'Amore 	for (spans = dp->spans; spans--; hp = hp->next)
354*698f87a4SGarrett D'Amore 		width += tp->tbl.cols[hp->ident].width + 3;
355*698f87a4SGarrett D'Amore 
356*698f87a4SGarrett D'Amore 	padr = width > len ? width - len : 0;
35795c635efSGarrett D'Amore 	padl = 0;
35895c635efSGarrett D'Amore 
35995c635efSGarrett D'Amore 	switch (dp->layout->pos) {
36095c635efSGarrett D'Amore 	case (TBL_CELL_LONG):
36195c635efSGarrett D'Amore 		padl = term_len(tp, 1);
36295c635efSGarrett D'Amore 		padr = padr > padl ? padr - padl : 0;
36395c635efSGarrett D'Amore 		break;
36495c635efSGarrett D'Amore 	case (TBL_CELL_CENTRE):
36595c635efSGarrett D'Amore 		if (2 > padr)
36695c635efSGarrett D'Amore 			break;
36795c635efSGarrett D'Amore 		padl = padr / 2;
36895c635efSGarrett D'Amore 		padr -= padl;
36995c635efSGarrett D'Amore 		break;
37095c635efSGarrett D'Amore 	case (TBL_CELL_RIGHT):
37195c635efSGarrett D'Amore 		padl = padr;
37295c635efSGarrett D'Amore 		padr = 0;
37395c635efSGarrett D'Amore 		break;
37495c635efSGarrett D'Amore 	default:
37595c635efSGarrett D'Amore 		break;
37695c635efSGarrett D'Amore 	}
37795c635efSGarrett D'Amore 
37895c635efSGarrett D'Amore 	tbl_char(tp, ASCII_NBRSP, padl);
37995c635efSGarrett D'Amore 	term_word(tp, dp->string);
38095c635efSGarrett D'Amore 	tbl_char(tp, ASCII_NBRSP, padr);
38195c635efSGarrett D'Amore }
38295c635efSGarrett D'Amore 
38395c635efSGarrett D'Amore static void
tbl_number(struct termp * tp,const struct tbl_opts * opts,const struct tbl_dat * dp,const struct roffcol * col)384*698f87a4SGarrett D'Amore tbl_number(struct termp *tp, const struct tbl_opts *opts,
38595c635efSGarrett D'Amore 		const struct tbl_dat *dp,
38695c635efSGarrett D'Amore 		const struct roffcol *col)
38795c635efSGarrett D'Amore {
38895c635efSGarrett D'Amore 	char		*cp;
38995c635efSGarrett D'Amore 	char		 buf[2];
39095c635efSGarrett D'Amore 	size_t		 sz, psz, ssz, d, padl;
39195c635efSGarrett D'Amore 	int		 i;
39295c635efSGarrett D'Amore 
39395c635efSGarrett D'Amore 	/*
39495c635efSGarrett D'Amore 	 * See calc_data_number().  Left-pad by taking the offset of our
39595c635efSGarrett D'Amore 	 * and the maximum decimal; right-pad by the remaining amount.
39695c635efSGarrett D'Amore 	 */
39795c635efSGarrett D'Amore 
39895c635efSGarrett D'Amore 	assert(dp->string);
39995c635efSGarrett D'Amore 
40095c635efSGarrett D'Amore 	sz = term_strlen(tp, dp->string);
40195c635efSGarrett D'Amore 
402*698f87a4SGarrett D'Amore 	buf[0] = opts->decimal;
40395c635efSGarrett D'Amore 	buf[1] = '\0';
40495c635efSGarrett D'Amore 
40595c635efSGarrett D'Amore 	psz = term_strlen(tp, buf);
40695c635efSGarrett D'Amore 
407*698f87a4SGarrett D'Amore 	if (NULL != (cp = strrchr(dp->string, opts->decimal))) {
40895c635efSGarrett D'Amore 		buf[1] = '\0';
40995c635efSGarrett D'Amore 		for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
41095c635efSGarrett D'Amore 			buf[0] = dp->string[i];
41195c635efSGarrett D'Amore 			ssz += term_strlen(tp, buf);
41295c635efSGarrett D'Amore 		}
41395c635efSGarrett D'Amore 		d = ssz + psz;
41495c635efSGarrett D'Amore 	} else
41595c635efSGarrett D'Amore 		d = sz + psz;
41695c635efSGarrett D'Amore 
41795c635efSGarrett D'Amore 	padl = col->decimal - d;
41895c635efSGarrett D'Amore 
41995c635efSGarrett D'Amore 	tbl_char(tp, ASCII_NBRSP, padl);
42095c635efSGarrett D'Amore 	term_word(tp, dp->string);
42195c635efSGarrett D'Amore 	if (col->width > sz + padl)
42295c635efSGarrett D'Amore 		tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
42395c635efSGarrett D'Amore }
42495c635efSGarrett D'Amore 
425