]> git.cameronkatri.com Git - mandoc.git/blob - out.c
In a2roffsu(), do not parse the number twice.
[mandoc.git] / out.c
1 /* $Id: out.c,v 1.55 2014/12/23 03:28:01 schwarze Exp $ */
2 /*
3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011, 2014 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 "config.h"
19
20 #include <sys/types.h>
21
22 #include <assert.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 /*
40 * Parse the *src string and store a scaling unit into *dst.
41 * If the string doesn't specify the unit, use the default.
42 * If no default is specified, fail.
43 * Return 1 on success and 0 on failure.
44 */
45 int
46 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
47 {
48 char *endptr;
49 double scale;
50 enum roffscale unit;
51
52 scale = strtod(src, &endptr);
53 if (endptr == src || (endptr[0] != '\0' && endptr[1] != '\0'))
54 return(0);
55
56 switch (*endptr) {
57 case 'c':
58 unit = SCALE_CM;
59 break;
60 case 'i':
61 unit = SCALE_IN;
62 break;
63 case 'P':
64 unit = SCALE_PC;
65 break;
66 case 'p':
67 unit = SCALE_PT;
68 break;
69 case 'f':
70 unit = SCALE_FS;
71 break;
72 case 'v':
73 unit = SCALE_VS;
74 break;
75 case 'm':
76 unit = SCALE_EM;
77 break;
78 case '\0':
79 if (SCALE_MAX == def)
80 return(0);
81 unit = def;
82 break;
83 case 'u':
84 unit = SCALE_BU;
85 break;
86 case 'M':
87 unit = SCALE_MM;
88 break;
89 case 'n':
90 unit = SCALE_EN;
91 break;
92 default:
93 return(0);
94 }
95
96 /* FIXME: do this in the caller. */
97 if (scale < 0.0)
98 scale = 0.0;
99 dst->scale = scale;
100 dst->unit = unit;
101 return(1);
102 }
103
104 /*
105 * Calculate the abstract widths and decimal positions of columns in a
106 * table. This routine allocates the columns structures then runs over
107 * all rows and cells in the table. The function pointers in "tbl" are
108 * used for the actual width calculations.
109 */
110 void
111 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
112 size_t totalwidth)
113 {
114 const struct tbl_dat *dp;
115 struct roffcol *col;
116 size_t ewidth, xwidth;
117 int spans;
118 int icol, maxcol, necol, nxcol;
119
120 /*
121 * Allocate the master column specifiers. These will hold the
122 * widths and decimal positions for all cells in the column. It
123 * must be freed and nullified by the caller.
124 */
125
126 assert(NULL == tbl->cols);
127 tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
128 sizeof(struct roffcol));
129
130 for (maxcol = -1; sp; sp = sp->next) {
131 if (TBL_SPAN_DATA != sp->pos)
132 continue;
133 spans = 1;
134 /*
135 * Account for the data cells in the layout, matching it
136 * to data cells in the data section.
137 */
138 for (dp = sp->first; dp; dp = dp->next) {
139 /* Do not used spanned cells in the calculation. */
140 if (0 < --spans)
141 continue;
142 spans = dp->spans;
143 if (1 < spans)
144 continue;
145 icol = dp->layout->head->ident;
146 if (maxcol < icol)
147 maxcol = icol;
148 col = tbl->cols + icol;
149 col->flags |= dp->layout->flags;
150 if (dp->layout->flags & TBL_CELL_WIGN)
151 continue;
152 tblcalc_data(tbl, col, sp->opts, dp);
153 }
154 }
155
156 /*
157 * Count columns to equalize and columns to maximize.
158 * Find maximum width of the columns to equalize.
159 * Find total width of the columns *not* to maximize.
160 */
161
162 necol = nxcol = 0;
163 ewidth = xwidth = 0;
164 for (icol = 0; icol <= maxcol; icol++) {
165 col = tbl->cols + icol;
166 if (col->flags & TBL_CELL_EQUAL) {
167 necol++;
168 if (ewidth < col->width)
169 ewidth = col->width;
170 }
171 if (col->flags & TBL_CELL_WMAX)
172 nxcol++;
173 else
174 xwidth += col->width;
175 }
176
177 /*
178 * Equalize columns, if requested for any of them.
179 * Update total width of the columns not to maximize.
180 */
181
182 if (necol) {
183 for (icol = 0; icol <= maxcol; icol++) {
184 col = tbl->cols + icol;
185 if ( ! (col->flags & TBL_CELL_EQUAL))
186 continue;
187 if (col->width == ewidth)
188 continue;
189 if (nxcol && totalwidth)
190 xwidth += ewidth - col->width;
191 col->width = ewidth;
192 }
193 }
194
195 /*
196 * If there are any columns to maximize, find the total
197 * available width, deducting 3n margins between columns.
198 * Distribute the available width evenly.
199 */
200
201 if (nxcol && totalwidth) {
202 xwidth = totalwidth - 3*maxcol - xwidth;
203 for (icol = 0; icol <= maxcol; icol++) {
204 col = tbl->cols + icol;
205 if ( ! (col->flags & TBL_CELL_WMAX))
206 continue;
207 col->width = xwidth / nxcol--;
208 xwidth -= col->width;
209 }
210 }
211 }
212
213 static void
214 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
215 const struct tbl_opts *opts, const struct tbl_dat *dp)
216 {
217 size_t sz;
218
219 /* Branch down into data sub-types. */
220
221 switch (dp->layout->pos) {
222 case TBL_CELL_HORIZ:
223 /* FALLTHROUGH */
224 case TBL_CELL_DHORIZ:
225 sz = (*tbl->len)(1, tbl->arg);
226 if (col->width < sz)
227 col->width = sz;
228 break;
229 case TBL_CELL_LONG:
230 /* FALLTHROUGH */
231 case TBL_CELL_CENTRE:
232 /* FALLTHROUGH */
233 case TBL_CELL_LEFT:
234 /* FALLTHROUGH */
235 case TBL_CELL_RIGHT:
236 tblcalc_literal(tbl, col, dp);
237 break;
238 case TBL_CELL_NUMBER:
239 tblcalc_number(tbl, col, opts, dp);
240 break;
241 case TBL_CELL_DOWN:
242 break;
243 default:
244 abort();
245 /* NOTREACHED */
246 }
247 }
248
249 static void
250 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
251 const struct tbl_dat *dp)
252 {
253 size_t sz;
254 const char *str;
255
256 str = dp->string ? dp->string : "";
257 sz = (*tbl->slen)(str, tbl->arg);
258
259 if (col->width < sz)
260 col->width = sz;
261 }
262
263 static void
264 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
265 const struct tbl_opts *opts, const struct tbl_dat *dp)
266 {
267 int i;
268 size_t sz, psz, ssz, d;
269 const char *str;
270 char *cp;
271 char buf[2];
272
273 /*
274 * First calculate number width and decimal place (last + 1 for
275 * non-decimal numbers). If the stored decimal is subsequent to
276 * ours, make our size longer by that difference
277 * (right-"shifting"); similarly, if ours is subsequent the
278 * stored, then extend the stored size by the difference.
279 * Finally, re-assign the stored values.
280 */
281
282 str = dp->string ? dp->string : "";
283 sz = (*tbl->slen)(str, tbl->arg);
284
285 /* FIXME: TBL_DATA_HORIZ et al.? */
286
287 buf[0] = opts->decimal;
288 buf[1] = '\0';
289
290 psz = (*tbl->slen)(buf, tbl->arg);
291
292 if (NULL != (cp = strrchr(str, opts->decimal))) {
293 buf[1] = '\0';
294 for (ssz = 0, i = 0; cp != &str[i]; i++) {
295 buf[0] = str[i];
296 ssz += (*tbl->slen)(buf, tbl->arg);
297 }
298 d = ssz + psz;
299 } else
300 d = sz + psz;
301
302 /* Adjust the settings for this column. */
303
304 if (col->decimal > d) {
305 sz += col->decimal - d;
306 d = col->decimal;
307 } else
308 col->width += d - col->decimal;
309
310 if (sz > col->width)
311 col->width = sz;
312 if (d > col->decimal)
313 col->decimal = d;
314 }