1*ffb8ebfaSGarrett D'Amore /* $Id: tbl_term.c,v 1.25 2013/05/31 21:37:17 schwarze Exp $ */
232a712daSGarrett D'Amore /*
332a712daSGarrett D'Amore * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*ffb8ebfaSGarrett D'Amore * Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
532a712daSGarrett D'Amore *
632a712daSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any
732a712daSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above
832a712daSGarrett D'Amore * copyright notice and this permission notice appear in all copies.
932a712daSGarrett D'Amore *
1032a712daSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1132a712daSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1232a712daSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1332a712daSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1432a712daSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1532a712daSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1632a712daSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1732a712daSGarrett D'Amore */
1832a712daSGarrett D'Amore #ifdef HAVE_CONFIG_H
1932a712daSGarrett D'Amore #include "config.h"
2032a712daSGarrett D'Amore #endif
2132a712daSGarrett D'Amore
2232a712daSGarrett D'Amore #include <assert.h>
2332a712daSGarrett D'Amore #include <stdio.h>
2432a712daSGarrett D'Amore #include <stdlib.h>
2532a712daSGarrett D'Amore #include <string.h>
2632a712daSGarrett D'Amore
2732a712daSGarrett D'Amore #include "mandoc.h"
2832a712daSGarrett D'Amore #include "out.h"
2932a712daSGarrett D'Amore #include "term.h"
3032a712daSGarrett D'Amore
3132a712daSGarrett D'Amore static size_t term_tbl_len(size_t, void *);
3232a712daSGarrett D'Amore static size_t term_tbl_strlen(const char *, void *);
3332a712daSGarrett D'Amore static void tbl_char(struct termp *, char, size_t);
34*ffb8ebfaSGarrett D'Amore static void tbl_data(struct termp *, const struct tbl_opts *,
3532a712daSGarrett D'Amore const struct tbl_dat *,
3632a712daSGarrett D'Amore const struct roffcol *);
3732a712daSGarrett D'Amore static size_t tbl_rulewidth(struct termp *, const struct tbl_head *);
3832a712daSGarrett D'Amore static void tbl_hframe(struct termp *, const struct tbl_span *, int);
3932a712daSGarrett D'Amore static void tbl_literal(struct termp *, const struct tbl_dat *,
4032a712daSGarrett D'Amore const struct roffcol *);
41*ffb8ebfaSGarrett D'Amore static void tbl_number(struct termp *, const struct tbl_opts *,
4232a712daSGarrett D'Amore const struct tbl_dat *,
4332a712daSGarrett D'Amore const struct roffcol *);
4432a712daSGarrett D'Amore static void tbl_hrule(struct termp *, const struct tbl_span *);
4532a712daSGarrett D'Amore static void tbl_vrule(struct termp *, const struct tbl_head *);
4632a712daSGarrett D'Amore
4732a712daSGarrett D'Amore
4832a712daSGarrett D'Amore static size_t
term_tbl_strlen(const char * p,void * arg)4932a712daSGarrett D'Amore term_tbl_strlen(const char *p, void *arg)
5032a712daSGarrett D'Amore {
5132a712daSGarrett D'Amore
5232a712daSGarrett D'Amore return(term_strlen((const struct termp *)arg, p));
5332a712daSGarrett D'Amore }
5432a712daSGarrett D'Amore
5532a712daSGarrett D'Amore static size_t
term_tbl_len(size_t sz,void * arg)5632a712daSGarrett D'Amore term_tbl_len(size_t sz, void *arg)
5732a712daSGarrett D'Amore {
5832a712daSGarrett D'Amore
5932a712daSGarrett D'Amore return(term_len((const struct termp *)arg, sz));
6032a712daSGarrett D'Amore }
6132a712daSGarrett D'Amore
6232a712daSGarrett D'Amore void
term_tbl(struct termp * tp,const struct tbl_span * sp)6332a712daSGarrett D'Amore term_tbl(struct termp *tp, const struct tbl_span *sp)
6432a712daSGarrett D'Amore {
6532a712daSGarrett D'Amore const struct tbl_head *hp;
6632a712daSGarrett D'Amore const struct tbl_dat *dp;
6732a712daSGarrett D'Amore struct roffcol *col;
6832a712daSGarrett D'Amore int spans;
6932a712daSGarrett D'Amore size_t rmargin, maxrmargin;
7032a712daSGarrett D'Amore
7132a712daSGarrett D'Amore rmargin = tp->rmargin;
7232a712daSGarrett D'Amore maxrmargin = tp->maxrmargin;
7332a712daSGarrett D'Amore
7432a712daSGarrett D'Amore tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
7532a712daSGarrett D'Amore
7632a712daSGarrett D'Amore /* Inhibit printing of spaces: we do padding ourselves. */
7732a712daSGarrett D'Amore
7832a712daSGarrett D'Amore tp->flags |= TERMP_NONOSPACE;
7932a712daSGarrett D'Amore tp->flags |= TERMP_NOSPACE;
8032a712daSGarrett D'Amore
8132a712daSGarrett D'Amore /*
8232a712daSGarrett D'Amore * The first time we're invoked for a given table block,
8332a712daSGarrett D'Amore * calculate the table widths and decimal positions.
8432a712daSGarrett D'Amore */
8532a712daSGarrett D'Amore
8632a712daSGarrett D'Amore if (TBL_SPAN_FIRST & sp->flags) {
8732a712daSGarrett D'Amore term_flushln(tp);
8832a712daSGarrett D'Amore
8932a712daSGarrett D'Amore tp->tbl.len = term_tbl_len;
9032a712daSGarrett D'Amore tp->tbl.slen = term_tbl_strlen;
9132a712daSGarrett D'Amore tp->tbl.arg = tp;
9232a712daSGarrett D'Amore
9332a712daSGarrett D'Amore tblcalc(&tp->tbl, sp);
9432a712daSGarrett D'Amore }
9532a712daSGarrett D'Amore
9632a712daSGarrett D'Amore /* Horizontal frame at the start of boxed tables. */
9732a712daSGarrett D'Amore
9832a712daSGarrett D'Amore if (TBL_SPAN_FIRST & sp->flags) {
99*ffb8ebfaSGarrett D'Amore if (TBL_OPT_DBOX & sp->opts->opts)
10032a712daSGarrett D'Amore tbl_hframe(tp, sp, 1);
101*ffb8ebfaSGarrett D'Amore if (TBL_OPT_DBOX & sp->opts->opts ||
102*ffb8ebfaSGarrett D'Amore TBL_OPT_BOX & sp->opts->opts)
10332a712daSGarrett D'Amore tbl_hframe(tp, sp, 0);
10432a712daSGarrett D'Amore }
10532a712daSGarrett D'Amore
10632a712daSGarrett D'Amore /* Vertical frame at the start of each row. */
10732a712daSGarrett D'Amore
108*ffb8ebfaSGarrett D'Amore if (TBL_OPT_BOX & sp->opts->opts || TBL_OPT_DBOX & sp->opts->opts)
10932a712daSGarrett D'Amore term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
11032a712daSGarrett D'Amore TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
11132a712daSGarrett D'Amore
11232a712daSGarrett D'Amore /*
11332a712daSGarrett D'Amore * Now print the actual data itself depending on the span type.
11432a712daSGarrett D'Amore * Spanner spans get a horizontal rule; data spanners have their
11532a712daSGarrett D'Amore * data printed by matching data to header.
11632a712daSGarrett D'Amore */
11732a712daSGarrett D'Amore
11832a712daSGarrett D'Amore switch (sp->pos) {
11932a712daSGarrett D'Amore case (TBL_SPAN_HORIZ):
12032a712daSGarrett D'Amore /* FALLTHROUGH */
12132a712daSGarrett D'Amore case (TBL_SPAN_DHORIZ):
12232a712daSGarrett D'Amore tbl_hrule(tp, sp);
12332a712daSGarrett D'Amore break;
12432a712daSGarrett D'Amore case (TBL_SPAN_DATA):
12532a712daSGarrett D'Amore /* Iterate over template headers. */
12632a712daSGarrett D'Amore dp = sp->first;
12732a712daSGarrett D'Amore spans = 0;
12832a712daSGarrett D'Amore for (hp = sp->head; hp; hp = hp->next) {
129*ffb8ebfaSGarrett D'Amore
13032a712daSGarrett D'Amore /*
13132a712daSGarrett D'Amore * If the current data header is invoked during
13232a712daSGarrett D'Amore * a spanner ("spans" > 0), don't emit anything
13332a712daSGarrett D'Amore * at all.
13432a712daSGarrett D'Amore */
13532a712daSGarrett D'Amore
13632a712daSGarrett D'Amore if (--spans >= 0)
13732a712daSGarrett D'Amore continue;
13832a712daSGarrett D'Amore
139*ffb8ebfaSGarrett D'Amore /* Separate columns. */
14032a712daSGarrett D'Amore
141*ffb8ebfaSGarrett D'Amore if (NULL != hp->prev)
142*ffb8ebfaSGarrett D'Amore tbl_vrule(tp, hp);
14332a712daSGarrett D'Amore
14432a712daSGarrett D'Amore col = &tp->tbl.cols[hp->ident];
145*ffb8ebfaSGarrett D'Amore tbl_data(tp, sp->opts, dp, col);
14632a712daSGarrett D'Amore
14732a712daSGarrett D'Amore /*
14832a712daSGarrett D'Amore * Go to the next data cell and assign the
14932a712daSGarrett D'Amore * number of subsequent spans, if applicable.
15032a712daSGarrett D'Amore */
15132a712daSGarrett D'Amore
15232a712daSGarrett D'Amore if (dp) {
15332a712daSGarrett D'Amore spans = dp->spans;
15432a712daSGarrett D'Amore dp = dp->next;
15532a712daSGarrett D'Amore }
15632a712daSGarrett D'Amore }
15732a712daSGarrett D'Amore break;
15832a712daSGarrett D'Amore }
15932a712daSGarrett D'Amore
16032a712daSGarrett D'Amore /* Vertical frame at the end of each row. */
16132a712daSGarrett D'Amore
162*ffb8ebfaSGarrett D'Amore if (TBL_OPT_BOX & sp->opts->opts || TBL_OPT_DBOX & sp->opts->opts)
16332a712daSGarrett D'Amore term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
16432a712daSGarrett D'Amore TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
16532a712daSGarrett D'Amore term_flushln(tp);
16632a712daSGarrett D'Amore
16732a712daSGarrett D'Amore /*
16832a712daSGarrett D'Amore * If we're the last row, clean up after ourselves: clear the
16932a712daSGarrett D'Amore * existing table configuration and set it to NULL.
17032a712daSGarrett D'Amore */
17132a712daSGarrett D'Amore
17232a712daSGarrett D'Amore if (TBL_SPAN_LAST & sp->flags) {
173*ffb8ebfaSGarrett D'Amore if (TBL_OPT_DBOX & sp->opts->opts ||
174*ffb8ebfaSGarrett D'Amore TBL_OPT_BOX & sp->opts->opts) {
17532a712daSGarrett D'Amore tbl_hframe(tp, sp, 0);
176*ffb8ebfaSGarrett D'Amore tp->skipvsp = 1;
177*ffb8ebfaSGarrett D'Amore }
178*ffb8ebfaSGarrett D'Amore if (TBL_OPT_DBOX & sp->opts->opts) {
17932a712daSGarrett D'Amore tbl_hframe(tp, sp, 1);
180*ffb8ebfaSGarrett D'Amore tp->skipvsp = 2;
181*ffb8ebfaSGarrett D'Amore }
18232a712daSGarrett D'Amore assert(tp->tbl.cols);
18332a712daSGarrett D'Amore free(tp->tbl.cols);
18432a712daSGarrett D'Amore tp->tbl.cols = NULL;
18532a712daSGarrett D'Amore }
18632a712daSGarrett D'Amore
18732a712daSGarrett D'Amore tp->flags &= ~TERMP_NONOSPACE;
18832a712daSGarrett D'Amore tp->rmargin = rmargin;
18932a712daSGarrett D'Amore tp->maxrmargin = maxrmargin;
19032a712daSGarrett D'Amore
19132a712daSGarrett D'Amore }
19232a712daSGarrett D'Amore
19332a712daSGarrett D'Amore /*
19432a712daSGarrett D'Amore * Horizontal rules extend across the entire table.
19532a712daSGarrett D'Amore * Calculate the width by iterating over columns.
19632a712daSGarrett D'Amore */
19732a712daSGarrett D'Amore static size_t
tbl_rulewidth(struct termp * tp,const struct tbl_head * hp)19832a712daSGarrett D'Amore tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
19932a712daSGarrett D'Amore {
20032a712daSGarrett D'Amore size_t width;
20132a712daSGarrett D'Amore
20232a712daSGarrett D'Amore width = tp->tbl.cols[hp->ident].width;
203*ffb8ebfaSGarrett D'Amore
20432a712daSGarrett D'Amore /* Account for leading blanks. */
205*ffb8ebfaSGarrett D'Amore if (hp->prev)
206*ffb8ebfaSGarrett D'Amore width += 2 - hp->vert;
207*ffb8ebfaSGarrett D'Amore
208*ffb8ebfaSGarrett D'Amore /* Account for trailing blank. */
20932a712daSGarrett D'Amore width++;
210*ffb8ebfaSGarrett D'Amore
21132a712daSGarrett D'Amore return(width);
21232a712daSGarrett D'Amore }
21332a712daSGarrett D'Amore
21432a712daSGarrett D'Amore /*
21532a712daSGarrett D'Amore * Rules inside the table can be single or double
21632a712daSGarrett D'Amore * and have crossings with vertical rules marked with pluses.
21732a712daSGarrett D'Amore */
21832a712daSGarrett D'Amore static void
tbl_hrule(struct termp * tp,const struct tbl_span * sp)21932a712daSGarrett D'Amore tbl_hrule(struct termp *tp, const struct tbl_span *sp)
22032a712daSGarrett D'Amore {
22132a712daSGarrett D'Amore const struct tbl_head *hp;
22232a712daSGarrett D'Amore char c;
22332a712daSGarrett D'Amore
22432a712daSGarrett D'Amore c = '-';
22532a712daSGarrett D'Amore if (TBL_SPAN_DHORIZ == sp->pos)
22632a712daSGarrett D'Amore c = '=';
22732a712daSGarrett D'Amore
228*ffb8ebfaSGarrett D'Amore for (hp = sp->head; hp; hp = hp->next) {
229*ffb8ebfaSGarrett D'Amore if (hp->prev && hp->vert)
230*ffb8ebfaSGarrett D'Amore tbl_char(tp, '+', hp->vert);
231*ffb8ebfaSGarrett D'Amore tbl_char(tp, c, tbl_rulewidth(tp, hp));
232*ffb8ebfaSGarrett D'Amore }
23332a712daSGarrett D'Amore }
23432a712daSGarrett D'Amore
23532a712daSGarrett D'Amore /*
23632a712daSGarrett D'Amore * Rules above and below the table are always single
23732a712daSGarrett D'Amore * and have an additional plus at the beginning and end.
23832a712daSGarrett D'Amore * For double frames, this function is called twice,
23932a712daSGarrett D'Amore * and the outer one does not have crossings.
24032a712daSGarrett D'Amore */
24132a712daSGarrett D'Amore static void
tbl_hframe(struct termp * tp,const struct tbl_span * sp,int outer)24232a712daSGarrett D'Amore tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
24332a712daSGarrett D'Amore {
24432a712daSGarrett D'Amore const struct tbl_head *hp;
24532a712daSGarrett D'Amore
24632a712daSGarrett D'Amore term_word(tp, "+");
247*ffb8ebfaSGarrett D'Amore for (hp = sp->head; hp; hp = hp->next) {
248*ffb8ebfaSGarrett D'Amore if (hp->prev && hp->vert)
249*ffb8ebfaSGarrett D'Amore tbl_char(tp, (outer ? '-' : '+'), hp->vert);
250*ffb8ebfaSGarrett D'Amore tbl_char(tp, '-', tbl_rulewidth(tp, hp));
251*ffb8ebfaSGarrett D'Amore }
25232a712daSGarrett D'Amore term_word(tp, "+");
25332a712daSGarrett D'Amore term_flushln(tp);
25432a712daSGarrett D'Amore }
25532a712daSGarrett D'Amore
25632a712daSGarrett D'Amore static void
tbl_data(struct termp * tp,const struct tbl_opts * opts,const struct tbl_dat * dp,const struct roffcol * col)257*ffb8ebfaSGarrett D'Amore tbl_data(struct termp *tp, const struct tbl_opts *opts,
25832a712daSGarrett D'Amore const struct tbl_dat *dp,
25932a712daSGarrett D'Amore const struct roffcol *col)
26032a712daSGarrett D'Amore {
26132a712daSGarrett D'Amore
26232a712daSGarrett D'Amore if (NULL == dp) {
26332a712daSGarrett D'Amore tbl_char(tp, ASCII_NBRSP, col->width);
26432a712daSGarrett D'Amore return;
26532a712daSGarrett D'Amore }
26632a712daSGarrett D'Amore assert(dp->layout);
26732a712daSGarrett D'Amore
26832a712daSGarrett D'Amore switch (dp->pos) {
26932a712daSGarrett D'Amore case (TBL_DATA_NONE):
27032a712daSGarrett D'Amore tbl_char(tp, ASCII_NBRSP, col->width);
27132a712daSGarrett D'Amore return;
27232a712daSGarrett D'Amore case (TBL_DATA_HORIZ):
27332a712daSGarrett D'Amore /* FALLTHROUGH */
27432a712daSGarrett D'Amore case (TBL_DATA_NHORIZ):
27532a712daSGarrett D'Amore tbl_char(tp, '-', col->width);
27632a712daSGarrett D'Amore return;
27732a712daSGarrett D'Amore case (TBL_DATA_NDHORIZ):
27832a712daSGarrett D'Amore /* FALLTHROUGH */
27932a712daSGarrett D'Amore case (TBL_DATA_DHORIZ):
28032a712daSGarrett D'Amore tbl_char(tp, '=', col->width);
28132a712daSGarrett D'Amore return;
28232a712daSGarrett D'Amore default:
28332a712daSGarrett D'Amore break;
28432a712daSGarrett D'Amore }
28532a712daSGarrett D'Amore
28632a712daSGarrett D'Amore switch (dp->layout->pos) {
28732a712daSGarrett D'Amore case (TBL_CELL_HORIZ):
28832a712daSGarrett D'Amore tbl_char(tp, '-', col->width);
28932a712daSGarrett D'Amore break;
29032a712daSGarrett D'Amore case (TBL_CELL_DHORIZ):
29132a712daSGarrett D'Amore tbl_char(tp, '=', col->width);
29232a712daSGarrett D'Amore break;
29332a712daSGarrett D'Amore case (TBL_CELL_LONG):
29432a712daSGarrett D'Amore /* FALLTHROUGH */
29532a712daSGarrett D'Amore case (TBL_CELL_CENTRE):
29632a712daSGarrett D'Amore /* FALLTHROUGH */
29732a712daSGarrett D'Amore case (TBL_CELL_LEFT):
29832a712daSGarrett D'Amore /* FALLTHROUGH */
29932a712daSGarrett D'Amore case (TBL_CELL_RIGHT):
30032a712daSGarrett D'Amore tbl_literal(tp, dp, col);
30132a712daSGarrett D'Amore break;
30232a712daSGarrett D'Amore case (TBL_CELL_NUMBER):
303*ffb8ebfaSGarrett D'Amore tbl_number(tp, opts, dp, col);
30432a712daSGarrett D'Amore break;
30532a712daSGarrett D'Amore case (TBL_CELL_DOWN):
30632a712daSGarrett D'Amore tbl_char(tp, ASCII_NBRSP, col->width);
30732a712daSGarrett D'Amore break;
30832a712daSGarrett D'Amore default:
30932a712daSGarrett D'Amore abort();
31032a712daSGarrett D'Amore /* NOTREACHED */
31132a712daSGarrett D'Amore }
31232a712daSGarrett D'Amore }
31332a712daSGarrett D'Amore
31432a712daSGarrett D'Amore static void
tbl_vrule(struct termp * tp,const struct tbl_head * hp)31532a712daSGarrett D'Amore tbl_vrule(struct termp *tp, const struct tbl_head *hp)
31632a712daSGarrett D'Amore {
31732a712daSGarrett D'Amore
318*ffb8ebfaSGarrett D'Amore tbl_char(tp, ASCII_NBRSP, 1);
319*ffb8ebfaSGarrett D'Amore if (0 < hp->vert)
320*ffb8ebfaSGarrett D'Amore tbl_char(tp, '|', hp->vert);
321*ffb8ebfaSGarrett D'Amore if (2 > hp->vert)
322*ffb8ebfaSGarrett D'Amore tbl_char(tp, ASCII_NBRSP, 2 - hp->vert);
32332a712daSGarrett D'Amore }
32432a712daSGarrett D'Amore
32532a712daSGarrett D'Amore static void
tbl_char(struct termp * tp,char c,size_t len)32632a712daSGarrett D'Amore tbl_char(struct termp *tp, char c, size_t len)
32732a712daSGarrett D'Amore {
32832a712daSGarrett D'Amore size_t i, sz;
32932a712daSGarrett D'Amore char cp[2];
33032a712daSGarrett D'Amore
33132a712daSGarrett D'Amore cp[0] = c;
33232a712daSGarrett D'Amore cp[1] = '\0';
33332a712daSGarrett D'Amore
33432a712daSGarrett D'Amore sz = term_strlen(tp, cp);
33532a712daSGarrett D'Amore
33632a712daSGarrett D'Amore for (i = 0; i < len; i += sz)
33732a712daSGarrett D'Amore term_word(tp, cp);
33832a712daSGarrett D'Amore }
33932a712daSGarrett D'Amore
34032a712daSGarrett D'Amore static void
tbl_literal(struct termp * tp,const struct tbl_dat * dp,const struct roffcol * col)34132a712daSGarrett D'Amore tbl_literal(struct termp *tp, const struct tbl_dat *dp,
34232a712daSGarrett D'Amore const struct roffcol *col)
34332a712daSGarrett D'Amore {
344*ffb8ebfaSGarrett D'Amore struct tbl_head *hp;
345*ffb8ebfaSGarrett D'Amore size_t width, len, padl, padr;
346*ffb8ebfaSGarrett D'Amore int spans;
34732a712daSGarrett D'Amore
34832a712daSGarrett D'Amore assert(dp->string);
34932a712daSGarrett D'Amore len = term_strlen(tp, dp->string);
350*ffb8ebfaSGarrett D'Amore
351*ffb8ebfaSGarrett D'Amore hp = dp->layout->head->next;
352*ffb8ebfaSGarrett D'Amore width = col->width;
353*ffb8ebfaSGarrett D'Amore for (spans = dp->spans; spans--; hp = hp->next)
354*ffb8ebfaSGarrett D'Amore width += tp->tbl.cols[hp->ident].width + 3;
355*ffb8ebfaSGarrett D'Amore
356*ffb8ebfaSGarrett D'Amore padr = width > len ? width - len : 0;
35732a712daSGarrett D'Amore padl = 0;
35832a712daSGarrett D'Amore
35932a712daSGarrett D'Amore switch (dp->layout->pos) {
36032a712daSGarrett D'Amore case (TBL_CELL_LONG):
36132a712daSGarrett D'Amore padl = term_len(tp, 1);
36232a712daSGarrett D'Amore padr = padr > padl ? padr - padl : 0;
36332a712daSGarrett D'Amore break;
36432a712daSGarrett D'Amore case (TBL_CELL_CENTRE):
36532a712daSGarrett D'Amore if (2 > padr)
36632a712daSGarrett D'Amore break;
36732a712daSGarrett D'Amore padl = padr / 2;
36832a712daSGarrett D'Amore padr -= padl;
36932a712daSGarrett D'Amore break;
37032a712daSGarrett D'Amore case (TBL_CELL_RIGHT):
37132a712daSGarrett D'Amore padl = padr;
37232a712daSGarrett D'Amore padr = 0;
37332a712daSGarrett D'Amore break;
37432a712daSGarrett D'Amore default:
37532a712daSGarrett D'Amore break;
37632a712daSGarrett D'Amore }
37732a712daSGarrett D'Amore
37832a712daSGarrett D'Amore tbl_char(tp, ASCII_NBRSP, padl);
37932a712daSGarrett D'Amore term_word(tp, dp->string);
38032a712daSGarrett D'Amore tbl_char(tp, ASCII_NBRSP, padr);
38132a712daSGarrett D'Amore }
38232a712daSGarrett D'Amore
38332a712daSGarrett D'Amore static void
tbl_number(struct termp * tp,const struct tbl_opts * opts,const struct tbl_dat * dp,const struct roffcol * col)384*ffb8ebfaSGarrett D'Amore tbl_number(struct termp *tp, const struct tbl_opts *opts,
38532a712daSGarrett D'Amore const struct tbl_dat *dp,
38632a712daSGarrett D'Amore const struct roffcol *col)
38732a712daSGarrett D'Amore {
38832a712daSGarrett D'Amore char *cp;
38932a712daSGarrett D'Amore char buf[2];
39032a712daSGarrett D'Amore size_t sz, psz, ssz, d, padl;
39132a712daSGarrett D'Amore int i;
39232a712daSGarrett D'Amore
39332a712daSGarrett D'Amore /*
39432a712daSGarrett D'Amore * See calc_data_number(). Left-pad by taking the offset of our
39532a712daSGarrett D'Amore * and the maximum decimal; right-pad by the remaining amount.
39632a712daSGarrett D'Amore */
39732a712daSGarrett D'Amore
39832a712daSGarrett D'Amore assert(dp->string);
39932a712daSGarrett D'Amore
40032a712daSGarrett D'Amore sz = term_strlen(tp, dp->string);
40132a712daSGarrett D'Amore
402*ffb8ebfaSGarrett D'Amore buf[0] = opts->decimal;
40332a712daSGarrett D'Amore buf[1] = '\0';
40432a712daSGarrett D'Amore
40532a712daSGarrett D'Amore psz = term_strlen(tp, buf);
40632a712daSGarrett D'Amore
407*ffb8ebfaSGarrett D'Amore if (NULL != (cp = strrchr(dp->string, opts->decimal))) {
40832a712daSGarrett D'Amore buf[1] = '\0';
40932a712daSGarrett D'Amore for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
41032a712daSGarrett D'Amore buf[0] = dp->string[i];
41132a712daSGarrett D'Amore ssz += term_strlen(tp, buf);
41232a712daSGarrett D'Amore }
41332a712daSGarrett D'Amore d = ssz + psz;
41432a712daSGarrett D'Amore } else
41532a712daSGarrett D'Amore d = sz + psz;
41632a712daSGarrett D'Amore
41732a712daSGarrett D'Amore padl = col->decimal - d;
41832a712daSGarrett D'Amore
41932a712daSGarrett D'Amore tbl_char(tp, ASCII_NBRSP, padl);
42032a712daSGarrett D'Amore term_word(tp, dp->string);
42132a712daSGarrett D'Amore if (col->width > sz + padl)
42232a712daSGarrett D'Amore tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
42332a712daSGarrett D'Amore }
42432a712daSGarrett D'Amore
425