xref: /freebsd/contrib/mandoc/out.c (revision 61d06d6bd19dafe8ea971dd43e8328fa1b473456)
1*61d06d6bSBaptiste Daroussin /*	$Id: out.c,v 1.70 2017/06/27 18:25:02 schwarze Exp $ */
2*61d06d6bSBaptiste Daroussin /*
3*61d06d6bSBaptiste Daroussin  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*61d06d6bSBaptiste Daroussin  * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
5*61d06d6bSBaptiste Daroussin  *
6*61d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
7*61d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
8*61d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
9*61d06d6bSBaptiste Daroussin  *
10*61d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*61d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*61d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*61d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*61d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*61d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*61d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*61d06d6bSBaptiste Daroussin  */
18*61d06d6bSBaptiste Daroussin #include "config.h"
19*61d06d6bSBaptiste Daroussin 
20*61d06d6bSBaptiste Daroussin #include <sys/types.h>
21*61d06d6bSBaptiste Daroussin 
22*61d06d6bSBaptiste Daroussin #include <assert.h>
23*61d06d6bSBaptiste Daroussin #include <stdint.h>
24*61d06d6bSBaptiste Daroussin #include <stdlib.h>
25*61d06d6bSBaptiste Daroussin #include <string.h>
26*61d06d6bSBaptiste Daroussin #include <time.h>
27*61d06d6bSBaptiste Daroussin 
28*61d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
29*61d06d6bSBaptiste Daroussin #include "mandoc.h"
30*61d06d6bSBaptiste Daroussin #include "out.h"
31*61d06d6bSBaptiste Daroussin 
32*61d06d6bSBaptiste Daroussin static	void	tblcalc_data(struct rofftbl *, struct roffcol *,
33*61d06d6bSBaptiste Daroussin 			const struct tbl_opts *, const struct tbl_dat *,
34*61d06d6bSBaptiste Daroussin 			size_t);
35*61d06d6bSBaptiste Daroussin static	void	tblcalc_literal(struct rofftbl *, struct roffcol *,
36*61d06d6bSBaptiste Daroussin 			const struct tbl_dat *, size_t);
37*61d06d6bSBaptiste Daroussin static	void	tblcalc_number(struct rofftbl *, struct roffcol *,
38*61d06d6bSBaptiste Daroussin 			const struct tbl_opts *, const struct tbl_dat *);
39*61d06d6bSBaptiste Daroussin 
40*61d06d6bSBaptiste Daroussin 
41*61d06d6bSBaptiste Daroussin /*
42*61d06d6bSBaptiste Daroussin  * Parse the *src string and store a scaling unit into *dst.
43*61d06d6bSBaptiste Daroussin  * If the string doesn't specify the unit, use the default.
44*61d06d6bSBaptiste Daroussin  * If no default is specified, fail.
45*61d06d6bSBaptiste Daroussin  * Return a pointer to the byte after the last byte used,
46*61d06d6bSBaptiste Daroussin  * or NULL on total failure.
47*61d06d6bSBaptiste Daroussin  */
48*61d06d6bSBaptiste Daroussin const char *
49*61d06d6bSBaptiste Daroussin a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
50*61d06d6bSBaptiste Daroussin {
51*61d06d6bSBaptiste Daroussin 	char		*endptr;
52*61d06d6bSBaptiste Daroussin 
53*61d06d6bSBaptiste Daroussin 	dst->unit = def == SCALE_MAX ? SCALE_BU : def;
54*61d06d6bSBaptiste Daroussin 	dst->scale = strtod(src, &endptr);
55*61d06d6bSBaptiste Daroussin 	if (endptr == src)
56*61d06d6bSBaptiste Daroussin 		return NULL;
57*61d06d6bSBaptiste Daroussin 
58*61d06d6bSBaptiste Daroussin 	switch (*endptr++) {
59*61d06d6bSBaptiste Daroussin 	case 'c':
60*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_CM;
61*61d06d6bSBaptiste Daroussin 		break;
62*61d06d6bSBaptiste Daroussin 	case 'i':
63*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_IN;
64*61d06d6bSBaptiste Daroussin 		break;
65*61d06d6bSBaptiste Daroussin 	case 'f':
66*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_FS;
67*61d06d6bSBaptiste Daroussin 		break;
68*61d06d6bSBaptiste Daroussin 	case 'M':
69*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_MM;
70*61d06d6bSBaptiste Daroussin 		break;
71*61d06d6bSBaptiste Daroussin 	case 'm':
72*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_EM;
73*61d06d6bSBaptiste Daroussin 		break;
74*61d06d6bSBaptiste Daroussin 	case 'n':
75*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_EN;
76*61d06d6bSBaptiste Daroussin 		break;
77*61d06d6bSBaptiste Daroussin 	case 'P':
78*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_PC;
79*61d06d6bSBaptiste Daroussin 		break;
80*61d06d6bSBaptiste Daroussin 	case 'p':
81*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_PT;
82*61d06d6bSBaptiste Daroussin 		break;
83*61d06d6bSBaptiste Daroussin 	case 'u':
84*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_BU;
85*61d06d6bSBaptiste Daroussin 		break;
86*61d06d6bSBaptiste Daroussin 	case 'v':
87*61d06d6bSBaptiste Daroussin 		dst->unit = SCALE_VS;
88*61d06d6bSBaptiste Daroussin 		break;
89*61d06d6bSBaptiste Daroussin 	default:
90*61d06d6bSBaptiste Daroussin 		endptr--;
91*61d06d6bSBaptiste Daroussin 		if (SCALE_MAX == def)
92*61d06d6bSBaptiste Daroussin 			return NULL;
93*61d06d6bSBaptiste Daroussin 		dst->unit = def;
94*61d06d6bSBaptiste Daroussin 		break;
95*61d06d6bSBaptiste Daroussin 	}
96*61d06d6bSBaptiste Daroussin 	return endptr;
97*61d06d6bSBaptiste Daroussin }
98*61d06d6bSBaptiste Daroussin 
99*61d06d6bSBaptiste Daroussin /*
100*61d06d6bSBaptiste Daroussin  * Calculate the abstract widths and decimal positions of columns in a
101*61d06d6bSBaptiste Daroussin  * table.  This routine allocates the columns structures then runs over
102*61d06d6bSBaptiste Daroussin  * all rows and cells in the table.  The function pointers in "tbl" are
103*61d06d6bSBaptiste Daroussin  * used for the actual width calculations.
104*61d06d6bSBaptiste Daroussin  */
105*61d06d6bSBaptiste Daroussin void
106*61d06d6bSBaptiste Daroussin tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
107*61d06d6bSBaptiste Daroussin     size_t offset, size_t rmargin)
108*61d06d6bSBaptiste Daroussin {
109*61d06d6bSBaptiste Daroussin 	struct roffsu		 su;
110*61d06d6bSBaptiste Daroussin 	const struct tbl_opts	*opts;
111*61d06d6bSBaptiste Daroussin 	const struct tbl_dat	*dp;
112*61d06d6bSBaptiste Daroussin 	struct roffcol		*col;
113*61d06d6bSBaptiste Daroussin 	size_t			 ewidth, xwidth;
114*61d06d6bSBaptiste Daroussin 	int			 spans;
115*61d06d6bSBaptiste Daroussin 	int			 icol, maxcol, necol, nxcol, quirkcol;
116*61d06d6bSBaptiste Daroussin 
117*61d06d6bSBaptiste Daroussin 	/*
118*61d06d6bSBaptiste Daroussin 	 * Allocate the master column specifiers.  These will hold the
119*61d06d6bSBaptiste Daroussin 	 * widths and decimal positions for all cells in the column.  It
120*61d06d6bSBaptiste Daroussin 	 * must be freed and nullified by the caller.
121*61d06d6bSBaptiste Daroussin 	 */
122*61d06d6bSBaptiste Daroussin 
123*61d06d6bSBaptiste Daroussin 	assert(NULL == tbl->cols);
124*61d06d6bSBaptiste Daroussin 	tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
125*61d06d6bSBaptiste Daroussin 	    sizeof(struct roffcol));
126*61d06d6bSBaptiste Daroussin 	opts = sp->opts;
127*61d06d6bSBaptiste Daroussin 
128*61d06d6bSBaptiste Daroussin 	for (maxcol = -1; sp; sp = sp->next) {
129*61d06d6bSBaptiste Daroussin 		if (TBL_SPAN_DATA != sp->pos)
130*61d06d6bSBaptiste Daroussin 			continue;
131*61d06d6bSBaptiste Daroussin 		spans = 1;
132*61d06d6bSBaptiste Daroussin 		/*
133*61d06d6bSBaptiste Daroussin 		 * Account for the data cells in the layout, matching it
134*61d06d6bSBaptiste Daroussin 		 * to data cells in the data section.
135*61d06d6bSBaptiste Daroussin 		 */
136*61d06d6bSBaptiste Daroussin 		for (dp = sp->first; dp; dp = dp->next) {
137*61d06d6bSBaptiste Daroussin 			/* Do not used spanned cells in the calculation. */
138*61d06d6bSBaptiste Daroussin 			if (0 < --spans)
139*61d06d6bSBaptiste Daroussin 				continue;
140*61d06d6bSBaptiste Daroussin 			spans = dp->spans;
141*61d06d6bSBaptiste Daroussin 			if (1 < spans)
142*61d06d6bSBaptiste Daroussin 				continue;
143*61d06d6bSBaptiste Daroussin 			icol = dp->layout->col;
144*61d06d6bSBaptiste Daroussin 			while (maxcol < icol)
145*61d06d6bSBaptiste Daroussin 				tbl->cols[++maxcol].spacing = SIZE_MAX;
146*61d06d6bSBaptiste Daroussin 			col = tbl->cols + icol;
147*61d06d6bSBaptiste Daroussin 			col->flags |= dp->layout->flags;
148*61d06d6bSBaptiste Daroussin 			if (dp->layout->flags & TBL_CELL_WIGN)
149*61d06d6bSBaptiste Daroussin 				continue;
150*61d06d6bSBaptiste Daroussin 			if (dp->layout->wstr != NULL &&
151*61d06d6bSBaptiste Daroussin 			    dp->layout->width == 0 &&
152*61d06d6bSBaptiste Daroussin 			    a2roffsu(dp->layout->wstr, &su, SCALE_EN)
153*61d06d6bSBaptiste Daroussin 			    != NULL)
154*61d06d6bSBaptiste Daroussin 				dp->layout->width =
155*61d06d6bSBaptiste Daroussin 				    (*tbl->sulen)(&su, tbl->arg);
156*61d06d6bSBaptiste Daroussin 			if (col->width < dp->layout->width)
157*61d06d6bSBaptiste Daroussin 				col->width = dp->layout->width;
158*61d06d6bSBaptiste Daroussin 			if (dp->layout->spacing != SIZE_MAX &&
159*61d06d6bSBaptiste Daroussin 			    (col->spacing == SIZE_MAX ||
160*61d06d6bSBaptiste Daroussin 			     col->spacing < dp->layout->spacing))
161*61d06d6bSBaptiste Daroussin 				col->spacing = dp->layout->spacing;
162*61d06d6bSBaptiste Daroussin 			tblcalc_data(tbl, col, opts, dp,
163*61d06d6bSBaptiste Daroussin 			    dp->block == 0 ? 0 :
164*61d06d6bSBaptiste Daroussin 			    dp->layout->width ? dp->layout->width :
165*61d06d6bSBaptiste Daroussin 			    rmargin ? (rmargin + sp->opts->cols / 2)
166*61d06d6bSBaptiste Daroussin 			    / (sp->opts->cols + 1) : 0);
167*61d06d6bSBaptiste Daroussin 		}
168*61d06d6bSBaptiste Daroussin 	}
169*61d06d6bSBaptiste Daroussin 
170*61d06d6bSBaptiste Daroussin 	/*
171*61d06d6bSBaptiste Daroussin 	 * Count columns to equalize and columns to maximize.
172*61d06d6bSBaptiste Daroussin 	 * Find maximum width of the columns to equalize.
173*61d06d6bSBaptiste Daroussin 	 * Find total width of the columns *not* to maximize.
174*61d06d6bSBaptiste Daroussin 	 */
175*61d06d6bSBaptiste Daroussin 
176*61d06d6bSBaptiste Daroussin 	necol = nxcol = 0;
177*61d06d6bSBaptiste Daroussin 	ewidth = xwidth = 0;
178*61d06d6bSBaptiste Daroussin 	for (icol = 0; icol <= maxcol; icol++) {
179*61d06d6bSBaptiste Daroussin 		col = tbl->cols + icol;
180*61d06d6bSBaptiste Daroussin 		if (col->spacing == SIZE_MAX || icol == maxcol)
181*61d06d6bSBaptiste Daroussin 			col->spacing = 3;
182*61d06d6bSBaptiste Daroussin 		if (col->flags & TBL_CELL_EQUAL) {
183*61d06d6bSBaptiste Daroussin 			necol++;
184*61d06d6bSBaptiste Daroussin 			if (ewidth < col->width)
185*61d06d6bSBaptiste Daroussin 				ewidth = col->width;
186*61d06d6bSBaptiste Daroussin 		}
187*61d06d6bSBaptiste Daroussin 		if (col->flags & TBL_CELL_WMAX)
188*61d06d6bSBaptiste Daroussin 			nxcol++;
189*61d06d6bSBaptiste Daroussin 		else
190*61d06d6bSBaptiste Daroussin 			xwidth += col->width;
191*61d06d6bSBaptiste Daroussin 	}
192*61d06d6bSBaptiste Daroussin 
193*61d06d6bSBaptiste Daroussin 	/*
194*61d06d6bSBaptiste Daroussin 	 * Equalize columns, if requested for any of them.
195*61d06d6bSBaptiste Daroussin 	 * Update total width of the columns not to maximize.
196*61d06d6bSBaptiste Daroussin 	 */
197*61d06d6bSBaptiste Daroussin 
198*61d06d6bSBaptiste Daroussin 	if (necol) {
199*61d06d6bSBaptiste Daroussin 		for (icol = 0; icol <= maxcol; icol++) {
200*61d06d6bSBaptiste Daroussin 			col = tbl->cols + icol;
201*61d06d6bSBaptiste Daroussin 			if ( ! (col->flags & TBL_CELL_EQUAL))
202*61d06d6bSBaptiste Daroussin 				continue;
203*61d06d6bSBaptiste Daroussin 			if (col->width == ewidth)
204*61d06d6bSBaptiste Daroussin 				continue;
205*61d06d6bSBaptiste Daroussin 			if (nxcol && rmargin)
206*61d06d6bSBaptiste Daroussin 				xwidth += ewidth - col->width;
207*61d06d6bSBaptiste Daroussin 			col->width = ewidth;
208*61d06d6bSBaptiste Daroussin 		}
209*61d06d6bSBaptiste Daroussin 	}
210*61d06d6bSBaptiste Daroussin 
211*61d06d6bSBaptiste Daroussin 	/*
212*61d06d6bSBaptiste Daroussin 	 * If there are any columns to maximize, find the total
213*61d06d6bSBaptiste Daroussin 	 * available width, deducting 3n margins between columns.
214*61d06d6bSBaptiste Daroussin 	 * Distribute the available width evenly.
215*61d06d6bSBaptiste Daroussin 	 */
216*61d06d6bSBaptiste Daroussin 
217*61d06d6bSBaptiste Daroussin 	if (nxcol && rmargin) {
218*61d06d6bSBaptiste Daroussin 		xwidth += 3*maxcol +
219*61d06d6bSBaptiste Daroussin 		    (opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
220*61d06d6bSBaptiste Daroussin 		     2 : !!opts->lvert + !!opts->rvert);
221*61d06d6bSBaptiste Daroussin 		if (rmargin <= offset + xwidth)
222*61d06d6bSBaptiste Daroussin 			return;
223*61d06d6bSBaptiste Daroussin 		xwidth = rmargin - offset - xwidth;
224*61d06d6bSBaptiste Daroussin 
225*61d06d6bSBaptiste Daroussin 		/*
226*61d06d6bSBaptiste Daroussin 		 * Emulate a bug in GNU tbl width calculation that
227*61d06d6bSBaptiste Daroussin 		 * manifests itself for large numbers of x-columns.
228*61d06d6bSBaptiste Daroussin 		 * Emulating it for 5 x-columns gives identical
229*61d06d6bSBaptiste Daroussin 		 * behaviour for up to 6 x-columns.
230*61d06d6bSBaptiste Daroussin 		 */
231*61d06d6bSBaptiste Daroussin 
232*61d06d6bSBaptiste Daroussin 		if (nxcol == 5) {
233*61d06d6bSBaptiste Daroussin 			quirkcol = xwidth % nxcol + 2;
234*61d06d6bSBaptiste Daroussin 			if (quirkcol != 3 && quirkcol != 4)
235*61d06d6bSBaptiste Daroussin 				quirkcol = -1;
236*61d06d6bSBaptiste Daroussin 		} else
237*61d06d6bSBaptiste Daroussin 			quirkcol = -1;
238*61d06d6bSBaptiste Daroussin 
239*61d06d6bSBaptiste Daroussin 		necol = 0;
240*61d06d6bSBaptiste Daroussin 		ewidth = 0;
241*61d06d6bSBaptiste Daroussin 		for (icol = 0; icol <= maxcol; icol++) {
242*61d06d6bSBaptiste Daroussin 			col = tbl->cols + icol;
243*61d06d6bSBaptiste Daroussin 			if ( ! (col->flags & TBL_CELL_WMAX))
244*61d06d6bSBaptiste Daroussin 				continue;
245*61d06d6bSBaptiste Daroussin 			col->width = (double)xwidth * ++necol / nxcol
246*61d06d6bSBaptiste Daroussin 			    - ewidth + 0.4995;
247*61d06d6bSBaptiste Daroussin 			if (necol == quirkcol)
248*61d06d6bSBaptiste Daroussin 				col->width--;
249*61d06d6bSBaptiste Daroussin 			ewidth += col->width;
250*61d06d6bSBaptiste Daroussin 		}
251*61d06d6bSBaptiste Daroussin 	}
252*61d06d6bSBaptiste Daroussin }
253*61d06d6bSBaptiste Daroussin 
254*61d06d6bSBaptiste Daroussin static void
255*61d06d6bSBaptiste Daroussin tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
256*61d06d6bSBaptiste Daroussin     const struct tbl_opts *opts, const struct tbl_dat *dp, size_t mw)
257*61d06d6bSBaptiste Daroussin {
258*61d06d6bSBaptiste Daroussin 	size_t		 sz;
259*61d06d6bSBaptiste Daroussin 
260*61d06d6bSBaptiste Daroussin 	/* Branch down into data sub-types. */
261*61d06d6bSBaptiste Daroussin 
262*61d06d6bSBaptiste Daroussin 	switch (dp->layout->pos) {
263*61d06d6bSBaptiste Daroussin 	case TBL_CELL_HORIZ:
264*61d06d6bSBaptiste Daroussin 	case TBL_CELL_DHORIZ:
265*61d06d6bSBaptiste Daroussin 		sz = (*tbl->len)(1, tbl->arg);
266*61d06d6bSBaptiste Daroussin 		if (col->width < sz)
267*61d06d6bSBaptiste Daroussin 			col->width = sz;
268*61d06d6bSBaptiste Daroussin 		break;
269*61d06d6bSBaptiste Daroussin 	case TBL_CELL_LONG:
270*61d06d6bSBaptiste Daroussin 	case TBL_CELL_CENTRE:
271*61d06d6bSBaptiste Daroussin 	case TBL_CELL_LEFT:
272*61d06d6bSBaptiste Daroussin 	case TBL_CELL_RIGHT:
273*61d06d6bSBaptiste Daroussin 		tblcalc_literal(tbl, col, dp, mw);
274*61d06d6bSBaptiste Daroussin 		break;
275*61d06d6bSBaptiste Daroussin 	case TBL_CELL_NUMBER:
276*61d06d6bSBaptiste Daroussin 		tblcalc_number(tbl, col, opts, dp);
277*61d06d6bSBaptiste Daroussin 		break;
278*61d06d6bSBaptiste Daroussin 	case TBL_CELL_DOWN:
279*61d06d6bSBaptiste Daroussin 		break;
280*61d06d6bSBaptiste Daroussin 	default:
281*61d06d6bSBaptiste Daroussin 		abort();
282*61d06d6bSBaptiste Daroussin 	}
283*61d06d6bSBaptiste Daroussin }
284*61d06d6bSBaptiste Daroussin 
285*61d06d6bSBaptiste Daroussin static void
286*61d06d6bSBaptiste Daroussin tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
287*61d06d6bSBaptiste Daroussin     const struct tbl_dat *dp, size_t mw)
288*61d06d6bSBaptiste Daroussin {
289*61d06d6bSBaptiste Daroussin 	const char	*str;	/* Beginning of the first line. */
290*61d06d6bSBaptiste Daroussin 	const char	*beg;	/* Beginning of the current line. */
291*61d06d6bSBaptiste Daroussin 	char		*end;	/* End of the current line. */
292*61d06d6bSBaptiste Daroussin 	size_t		 lsz;	/* Length of the current line. */
293*61d06d6bSBaptiste Daroussin 	size_t		 wsz;	/* Length of the current word. */
294*61d06d6bSBaptiste Daroussin 
295*61d06d6bSBaptiste Daroussin 	if (dp->string == NULL || *dp->string == '\0')
296*61d06d6bSBaptiste Daroussin 		return;
297*61d06d6bSBaptiste Daroussin 	str = mw ? mandoc_strdup(dp->string) : dp->string;
298*61d06d6bSBaptiste Daroussin 	lsz = 0;
299*61d06d6bSBaptiste Daroussin 	for (beg = str; beg != NULL && *beg != '\0'; beg = end) {
300*61d06d6bSBaptiste Daroussin 		end = mw ? strchr(beg, ' ') : NULL;
301*61d06d6bSBaptiste Daroussin 		if (end != NULL) {
302*61d06d6bSBaptiste Daroussin 			*end++ = '\0';
303*61d06d6bSBaptiste Daroussin 			while (*end == ' ')
304*61d06d6bSBaptiste Daroussin 				end++;
305*61d06d6bSBaptiste Daroussin 		}
306*61d06d6bSBaptiste Daroussin 		wsz = (*tbl->slen)(beg, tbl->arg);
307*61d06d6bSBaptiste Daroussin 		if (mw && lsz && lsz + 1 + wsz <= mw)
308*61d06d6bSBaptiste Daroussin 			lsz += 1 + wsz;
309*61d06d6bSBaptiste Daroussin 		else
310*61d06d6bSBaptiste Daroussin 			lsz = wsz;
311*61d06d6bSBaptiste Daroussin 		if (col->width < lsz)
312*61d06d6bSBaptiste Daroussin 			col->width = lsz;
313*61d06d6bSBaptiste Daroussin 	}
314*61d06d6bSBaptiste Daroussin 	if (mw)
315*61d06d6bSBaptiste Daroussin 		free((void *)str);
316*61d06d6bSBaptiste Daroussin }
317*61d06d6bSBaptiste Daroussin 
318*61d06d6bSBaptiste Daroussin static void
319*61d06d6bSBaptiste Daroussin tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
320*61d06d6bSBaptiste Daroussin 		const struct tbl_opts *opts, const struct tbl_dat *dp)
321*61d06d6bSBaptiste Daroussin {
322*61d06d6bSBaptiste Daroussin 	int		 i;
323*61d06d6bSBaptiste Daroussin 	size_t		 sz, psz, ssz, d;
324*61d06d6bSBaptiste Daroussin 	const char	*str;
325*61d06d6bSBaptiste Daroussin 	char		*cp;
326*61d06d6bSBaptiste Daroussin 	char		 buf[2];
327*61d06d6bSBaptiste Daroussin 
328*61d06d6bSBaptiste Daroussin 	/*
329*61d06d6bSBaptiste Daroussin 	 * First calculate number width and decimal place (last + 1 for
330*61d06d6bSBaptiste Daroussin 	 * non-decimal numbers).  If the stored decimal is subsequent to
331*61d06d6bSBaptiste Daroussin 	 * ours, make our size longer by that difference
332*61d06d6bSBaptiste Daroussin 	 * (right-"shifting"); similarly, if ours is subsequent the
333*61d06d6bSBaptiste Daroussin 	 * stored, then extend the stored size by the difference.
334*61d06d6bSBaptiste Daroussin 	 * Finally, re-assign the stored values.
335*61d06d6bSBaptiste Daroussin 	 */
336*61d06d6bSBaptiste Daroussin 
337*61d06d6bSBaptiste Daroussin 	str = dp->string ? dp->string : "";
338*61d06d6bSBaptiste Daroussin 	sz = (*tbl->slen)(str, tbl->arg);
339*61d06d6bSBaptiste Daroussin 
340*61d06d6bSBaptiste Daroussin 	/* FIXME: TBL_DATA_HORIZ et al.? */
341*61d06d6bSBaptiste Daroussin 
342*61d06d6bSBaptiste Daroussin 	buf[0] = opts->decimal;
343*61d06d6bSBaptiste Daroussin 	buf[1] = '\0';
344*61d06d6bSBaptiste Daroussin 
345*61d06d6bSBaptiste Daroussin 	psz = (*tbl->slen)(buf, tbl->arg);
346*61d06d6bSBaptiste Daroussin 
347*61d06d6bSBaptiste Daroussin 	if (NULL != (cp = strrchr(str, opts->decimal))) {
348*61d06d6bSBaptiste Daroussin 		buf[1] = '\0';
349*61d06d6bSBaptiste Daroussin 		for (ssz = 0, i = 0; cp != &str[i]; i++) {
350*61d06d6bSBaptiste Daroussin 			buf[0] = str[i];
351*61d06d6bSBaptiste Daroussin 			ssz += (*tbl->slen)(buf, tbl->arg);
352*61d06d6bSBaptiste Daroussin 		}
353*61d06d6bSBaptiste Daroussin 		d = ssz + psz;
354*61d06d6bSBaptiste Daroussin 	} else
355*61d06d6bSBaptiste Daroussin 		d = sz + psz;
356*61d06d6bSBaptiste Daroussin 
357*61d06d6bSBaptiste Daroussin 	/* Adjust the settings for this column. */
358*61d06d6bSBaptiste Daroussin 
359*61d06d6bSBaptiste Daroussin 	if (col->decimal > d) {
360*61d06d6bSBaptiste Daroussin 		sz += col->decimal - d;
361*61d06d6bSBaptiste Daroussin 		d = col->decimal;
362*61d06d6bSBaptiste Daroussin 	} else
363*61d06d6bSBaptiste Daroussin 		col->width += d - col->decimal;
364*61d06d6bSBaptiste Daroussin 
365*61d06d6bSBaptiste Daroussin 	if (sz > col->width)
366*61d06d6bSBaptiste Daroussin 		col->width = sz;
367*61d06d6bSBaptiste Daroussin 	if (d > col->decimal)
368*61d06d6bSBaptiste Daroussin 		col->decimal = d;
369*61d06d6bSBaptiste Daroussin }
370