xref: /openbsd-src/usr.bin/mandoc/out.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$Id: out.c,v 1.20 2014/03/21 22:17:01 schwarze Exp $ */
2 /*
3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2011 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 <ctype.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 
27 #include "mandoc_aux.h"
28 #include "mandoc.h"
29 #include "out.h"
30 
31 static	void	tblcalc_data(struct rofftbl *, struct roffcol *,
32 			const struct tbl_opts *, const struct tbl_dat *);
33 static	void	tblcalc_literal(struct rofftbl *, struct roffcol *,
34 			const struct tbl_dat *);
35 static	void	tblcalc_number(struct rofftbl *, struct roffcol *,
36 			const struct tbl_opts *, const struct tbl_dat *);
37 
38 /*
39  * Convert a `scaling unit' to a consistent form, or fail.  Scaling
40  * units are documented in groff.7, mdoc.7, man.7.
41  */
42 int
43 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
44 {
45 	char		 buf[BUFSIZ], hasd;
46 	int		 i;
47 	enum roffscale	 unit;
48 
49 	if ('\0' == *src)
50 		return(0);
51 
52 	i = hasd = 0;
53 
54 	switch (*src) {
55 	case ('+'):
56 		src++;
57 		break;
58 	case ('-'):
59 		buf[i++] = *src++;
60 		break;
61 	default:
62 		break;
63 	}
64 
65 	if ('\0' == *src)
66 		return(0);
67 
68 	while (i < BUFSIZ) {
69 		if ( ! isdigit((unsigned char)*src)) {
70 			if ('.' != *src)
71 				break;
72 			else if (hasd)
73 				break;
74 			else
75 				hasd = 1;
76 		}
77 		buf[i++] = *src++;
78 	}
79 
80 	if (BUFSIZ == i || (*src && *(src + 1)))
81 		return(0);
82 
83 	buf[i] = '\0';
84 
85 	switch (*src) {
86 	case ('c'):
87 		unit = SCALE_CM;
88 		break;
89 	case ('i'):
90 		unit = SCALE_IN;
91 		break;
92 	case ('P'):
93 		unit = SCALE_PC;
94 		break;
95 	case ('p'):
96 		unit = SCALE_PT;
97 		break;
98 	case ('f'):
99 		unit = SCALE_FS;
100 		break;
101 	case ('v'):
102 		unit = SCALE_VS;
103 		break;
104 	case ('m'):
105 		unit = SCALE_EM;
106 		break;
107 	case ('\0'):
108 		if (SCALE_MAX == def)
109 			return(0);
110 		unit = SCALE_BU;
111 		break;
112 	case ('u'):
113 		unit = SCALE_BU;
114 		break;
115 	case ('M'):
116 		unit = SCALE_MM;
117 		break;
118 	case ('n'):
119 		unit = SCALE_EN;
120 		break;
121 	default:
122 		return(0);
123 	}
124 
125 	/* FIXME: do this in the caller. */
126 	if ((dst->scale = atof(buf)) < 0)
127 		dst->scale = 0;
128 	dst->unit = unit;
129 	return(1);
130 }
131 
132 /*
133  * Calculate the abstract widths and decimal positions of columns in a
134  * table.  This routine allocates the columns structures then runs over
135  * all rows and cells in the table.  The function pointers in "tbl" are
136  * used for the actual width calculations.
137  */
138 void
139 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
140 {
141 	const struct tbl_dat	*dp;
142 	struct roffcol		*col;
143 	int			 spans;
144 
145 	/*
146 	 * Allocate the master column specifiers.  These will hold the
147 	 * widths and decimal positions for all cells in the column.  It
148 	 * must be freed and nullified by the caller.
149 	 */
150 
151 	assert(NULL == tbl->cols);
152 	tbl->cols = mandoc_calloc
153 		((size_t)sp->opts->cols, sizeof(struct roffcol));
154 
155 	for ( ; sp; sp = sp->next) {
156 		if (TBL_SPAN_DATA != sp->pos)
157 			continue;
158 		spans = 1;
159 		/*
160 		 * Account for the data cells in the layout, matching it
161 		 * to data cells in the data section.
162 		 */
163 		for (dp = sp->first; dp; dp = dp->next) {
164 			/* Do not used spanned cells in the calculation. */
165 			if (0 < --spans)
166 				continue;
167 			spans = dp->spans;
168 			if (1 < spans)
169 				continue;
170 			assert(dp->layout);
171 			col = &tbl->cols[dp->layout->head->ident];
172 			tblcalc_data(tbl, col, sp->opts, dp);
173 		}
174 	}
175 }
176 
177 static void
178 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
179 		const struct tbl_opts *opts, const struct tbl_dat *dp)
180 {
181 	size_t		 sz;
182 
183 	/* Branch down into data sub-types. */
184 
185 	switch (dp->layout->pos) {
186 	case (TBL_CELL_HORIZ):
187 		/* FALLTHROUGH */
188 	case (TBL_CELL_DHORIZ):
189 		sz = (*tbl->len)(1, tbl->arg);
190 		if (col->width < sz)
191 			col->width = sz;
192 		break;
193 	case (TBL_CELL_LONG):
194 		/* FALLTHROUGH */
195 	case (TBL_CELL_CENTRE):
196 		/* FALLTHROUGH */
197 	case (TBL_CELL_LEFT):
198 		/* FALLTHROUGH */
199 	case (TBL_CELL_RIGHT):
200 		tblcalc_literal(tbl, col, dp);
201 		break;
202 	case (TBL_CELL_NUMBER):
203 		tblcalc_number(tbl, col, opts, dp);
204 		break;
205 	case (TBL_CELL_DOWN):
206 		break;
207 	default:
208 		abort();
209 		/* NOTREACHED */
210 	}
211 }
212 
213 static void
214 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
215 		const struct tbl_dat *dp)
216 {
217 	size_t		 sz;
218 	const char	*str;
219 
220 	str = dp->string ? dp->string : "";
221 	sz = (*tbl->slen)(str, tbl->arg);
222 
223 	if (col->width < sz)
224 		col->width = sz;
225 }
226 
227 static void
228 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
229 		const struct tbl_opts *opts, const struct tbl_dat *dp)
230 {
231 	int 		 i;
232 	size_t		 sz, psz, ssz, d;
233 	const char	*str;
234 	char		*cp;
235 	char		 buf[2];
236 
237 	/*
238 	 * First calculate number width and decimal place (last + 1 for
239 	 * non-decimal numbers).  If the stored decimal is subsequent to
240 	 * ours, make our size longer by that difference
241 	 * (right-"shifting"); similarly, if ours is subsequent the
242 	 * stored, then extend the stored size by the difference.
243 	 * Finally, re-assign the stored values.
244 	 */
245 
246 	str = dp->string ? dp->string : "";
247 	sz = (*tbl->slen)(str, tbl->arg);
248 
249 	/* FIXME: TBL_DATA_HORIZ et al.? */
250 
251 	buf[0] = opts->decimal;
252 	buf[1] = '\0';
253 
254 	psz = (*tbl->slen)(buf, tbl->arg);
255 
256 	if (NULL != (cp = strrchr(str, opts->decimal))) {
257 		buf[1] = '\0';
258 		for (ssz = 0, i = 0; cp != &str[i]; i++) {
259 			buf[0] = str[i];
260 			ssz += (*tbl->slen)(buf, tbl->arg);
261 		}
262 		d = ssz + psz;
263 	} else
264 		d = sz + psz;
265 
266 	/* Adjust the settings for this column. */
267 
268 	if (col->decimal > d) {
269 		sz += col->decimal - d;
270 		d = col->decimal;
271 	} else
272 		col->width += d - col->decimal;
273 
274 	if (sz > col->width)
275 		col->width = sz;
276 	if (d > col->decimal)
277 		col->decimal = d;
278 }
279