GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: out.c,v 1.42 2017/06/27 18:23:29 schwarze Exp $ */ |
||
2 |
/* |
||
3 |
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
||
4 |
* Copyright (c) 2011, 2014, 2015, 2017 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 <stdint.h> |
||
22 |
#include <stdlib.h> |
||
23 |
#include <string.h> |
||
24 |
#include <time.h> |
||
25 |
|||
26 |
#include "mandoc_aux.h" |
||
27 |
#include "mandoc.h" |
||
28 |
#include "out.h" |
||
29 |
|||
30 |
static void tblcalc_data(struct rofftbl *, struct roffcol *, |
||
31 |
const struct tbl_opts *, const struct tbl_dat *, |
||
32 |
size_t); |
||
33 |
static void tblcalc_literal(struct rofftbl *, struct roffcol *, |
||
34 |
const struct tbl_dat *, size_t); |
||
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 a pointer to the byte after the last byte used, |
||
44 |
* or NULL on total failure. |
||
45 |
*/ |
||
46 |
const char * |
||
47 |
a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) |
||
48 |
{ |
||
49 |
76488 |
char *endptr; |
|
50 |
|||
51 |
38244 |
dst->unit = def == SCALE_MAX ? SCALE_BU : def; |
|
52 |
38244 |
dst->scale = strtod(src, &endptr); |
|
53 |
✓✓ | 38244 |
if (endptr == src) |
54 |
1683 |
return NULL; |
|
55 |
|||
56 |
✓✓✓✓ ✓✓✓✓ ✓✓✓ |
36561 |
switch (*endptr++) { |
57 |
case 'c': |
||
58 |
dst->unit = SCALE_CM; |
||
59 |
45 |
break; |
|
60 |
case 'i': |
||
61 |
dst->unit = SCALE_IN; |
||
62 |
20127 |
break; |
|
63 |
case 'f': |
||
64 |
dst->unit = SCALE_FS; |
||
65 |
9 |
break; |
|
66 |
case 'M': |
||
67 |
dst->unit = SCALE_MM; |
||
68 |
45 |
break; |
|
69 |
case 'm': |
||
70 |
dst->unit = SCALE_EM; |
||
71 |
45 |
break; |
|
72 |
case 'n': |
||
73 |
dst->unit = SCALE_EN; |
||
74 |
12933 |
break; |
|
75 |
case 'P': |
||
76 |
dst->unit = SCALE_PC; |
||
77 |
45 |
break; |
|
78 |
case 'p': |
||
79 |
dst->unit = SCALE_PT; |
||
80 |
54 |
break; |
|
81 |
case 'u': |
||
82 |
dst->unit = SCALE_BU; |
||
83 |
45 |
break; |
|
84 |
case 'v': |
||
85 |
dst->unit = SCALE_VS; |
||
86 |
2466 |
break; |
|
87 |
default: |
||
88 |
747 |
endptr--; |
|
89 |
✓✓ | 747 |
if (SCALE_MAX == def) |
90 |
18 |
return NULL; |
|
91 |
dst->unit = def; |
||
92 |
729 |
break; |
|
93 |
} |
||
94 |
36543 |
return endptr; |
|
95 |
38244 |
} |
|
96 |
|||
97 |
/* |
||
98 |
* Calculate the abstract widths and decimal positions of columns in a |
||
99 |
* table. This routine allocates the columns structures then runs over |
||
100 |
* all rows and cells in the table. The function pointers in "tbl" are |
||
101 |
* used for the actual width calculations. |
||
102 |
*/ |
||
103 |
void |
||
104 |
tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, |
||
105 |
size_t offset, size_t rmargin) |
||
106 |
{ |
||
107 |
13176 |
struct roffsu su; |
|
108 |
const struct tbl_opts *opts; |
||
109 |
const struct tbl_dat *dp; |
||
110 |
struct roffcol *col; |
||
111 |
size_t ewidth, xwidth; |
||
112 |
int spans; |
||
113 |
int icol, maxcol, necol, nxcol, quirkcol; |
||
114 |
|||
115 |
/* |
||
116 |
* Allocate the master column specifiers. These will hold the |
||
117 |
* widths and decimal positions for all cells in the column. It |
||
118 |
* must be freed and nullified by the caller. |
||
119 |
*/ |
||
120 |
|||
121 |
✗✓ | 6588 |
assert(NULL == tbl->cols); |
122 |
6588 |
tbl->cols = mandoc_calloc((size_t)sp->opts->cols, |
|
123 |
sizeof(struct roffcol)); |
||
124 |
6588 |
opts = sp->opts; |
|
125 |
|||
126 |
✓✓ | 55674 |
for (maxcol = -1; sp; sp = sp->next) { |
127 |
✓✓ | 21249 |
if (TBL_SPAN_DATA != sp->pos) |
128 |
continue; |
||
129 |
spans = 1; |
||
130 |
/* |
||
131 |
* Account for the data cells in the layout, matching it |
||
132 |
* to data cells in the data section. |
||
133 |
*/ |
||
134 |
✓✓ | 149130 |
for (dp = sp->first; dp; dp = dp->next) { |
135 |
/* Do not used spanned cells in the calculation. */ |
||
136 |
✓✓ | 53550 |
if (0 < --spans) |
137 |
continue; |
||
138 |
53541 |
spans = dp->spans; |
|
139 |
✓✓ | 53541 |
if (1 < spans) |
140 |
continue; |
||
141 |
53532 |
icol = dp->layout->col; |
|
142 |
✓✓ | 142002 |
while (maxcol < icol) |
143 |
17469 |
tbl->cols[++maxcol].spacing = SIZE_MAX; |
|
144 |
53532 |
col = tbl->cols + icol; |
|
145 |
53532 |
col->flags |= dp->layout->flags; |
|
146 |
✓✓ | 53532 |
if (dp->layout->flags & TBL_CELL_WIGN) |
147 |
continue; |
||
148 |
✓✓✓✗ |
53550 |
if (dp->layout->wstr != NULL && |
149 |
✓✓ | 72 |
dp->layout->width == 0 && |
150 |
54 |
a2roffsu(dp->layout->wstr, &su, SCALE_EN) |
|
151 |
54 |
!= NULL) |
|
152 |
54 |
dp->layout->width = |
|
153 |
54 |
(*tbl->sulen)(&su, tbl->arg); |
|
154 |
✓✓ | 53496 |
if (col->width < dp->layout->width) |
155 |
54 |
col->width = dp->layout->width; |
|
156 |
✓✓✗✓ |
53658 |
if (dp->layout->spacing != SIZE_MAX && |
157 |
✓✓ | 252 |
(col->spacing == SIZE_MAX || |
158 |
162 |
col->spacing < dp->layout->spacing)) |
|
159 |
90 |
col->spacing = dp->layout->spacing; |
|
160 |
53496 |
tblcalc_data(tbl, col, opts, dp, |
|
161 |
✓✓ | 106992 |
dp->block == 0 ? 0 : |
162 |
✓✓ | 297 |
dp->layout->width ? dp->layout->width : |
163 |
✓✗ | 675 |
rmargin ? (rmargin + sp->opts->cols / 2) |
164 |
225 |
/ (sp->opts->cols + 1) : 0); |
|
165 |
53496 |
} |
|
166 |
} |
||
167 |
|||
168 |
/* |
||
169 |
* Count columns to equalize and columns to maximize. |
||
170 |
* Find maximum width of the columns to equalize. |
||
171 |
* Find total width of the columns *not* to maximize. |
||
172 |
*/ |
||
173 |
|||
174 |
necol = nxcol = 0; |
||
175 |
ewidth = xwidth = 0; |
||
176 |
✓✓ | 48114 |
for (icol = 0; icol <= maxcol; icol++) { |
177 |
17469 |
col = tbl->cols + icol; |
|
178 |
✓✓✓✓ |
17559 |
if (col->spacing == SIZE_MAX || icol == maxcol) |
179 |
17397 |
col->spacing = 3; |
|
180 |
✗✓ | 17469 |
if (col->flags & TBL_CELL_EQUAL) { |
181 |
necol++; |
||
182 |
if (ewidth < col->width) |
||
183 |
ewidth = col->width; |
||
184 |
} |
||
185 |
✓✓ | 17469 |
if (col->flags & TBL_CELL_WMAX) |
186 |
1656 |
nxcol++; |
|
187 |
else |
||
188 |
15813 |
xwidth += col->width; |
|
189 |
} |
||
190 |
|||
191 |
/* |
||
192 |
* Equalize columns, if requested for any of them. |
||
193 |
* Update total width of the columns not to maximize. |
||
194 |
*/ |
||
195 |
|||
196 |
✗✓ | 6588 |
if (necol) { |
197 |
for (icol = 0; icol <= maxcol; icol++) { |
||
198 |
col = tbl->cols + icol; |
||
199 |
if ( ! (col->flags & TBL_CELL_EQUAL)) |
||
200 |
continue; |
||
201 |
if (col->width == ewidth) |
||
202 |
continue; |
||
203 |
if (nxcol && rmargin) |
||
204 |
xwidth += ewidth - col->width; |
||
205 |
col->width = ewidth; |
||
206 |
} |
||
207 |
} |
||
208 |
|||
209 |
/* |
||
210 |
* If there are any columns to maximize, find the total |
||
211 |
* available width, deducting 3n margins between columns. |
||
212 |
* Distribute the available width evenly. |
||
213 |
*/ |
||
214 |
|||
215 |
✓✓ | 6588 |
if (nxcol && rmargin) { |
216 |
810 |
xwidth += 3*maxcol + |
|
217 |
✓✗ | 1215 |
(opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ? |
218 |
405 |
2 : !!opts->lvert + !!opts->rvert); |
|
219 |
✓✓ | 405 |
if (rmargin <= offset + xwidth) |
220 |
9 |
return; |
|
221 |
396 |
xwidth = rmargin - offset - xwidth; |
|
222 |
|||
223 |
/* |
||
224 |
* Emulate a bug in GNU tbl width calculation that |
||
225 |
* manifests itself for large numbers of x-columns. |
||
226 |
* Emulating it for 5 x-columns gives identical |
||
227 |
* behaviour for up to 6 x-columns. |
||
228 |
*/ |
||
229 |
|||
230 |
✓✓ | 396 |
if (nxcol == 5) { |
231 |
90 |
quirkcol = xwidth % nxcol + 2; |
|
232 |
90 |
if (quirkcol != 3 && quirkcol != 4) |
|
233 |
quirkcol = -1; |
||
234 |
} else |
||
235 |
quirkcol = -1; |
||
236 |
|||
237 |
necol = 0; |
||
238 |
ewidth = 0; |
||
239 |
✓✓ | 4860 |
for (icol = 0; icol <= maxcol; icol++) { |
240 |
2034 |
col = tbl->cols + icol; |
|
241 |
✓✓ | 2034 |
if ( ! (col->flags & TBL_CELL_WMAX)) |
242 |
continue; |
||
243 |
3276 |
col->width = (double)xwidth * ++necol / nxcol |
|
244 |
1638 |
- ewidth + 0.4995; |
|
245 |
✓✓ | 1638 |
if (necol == quirkcol) |
246 |
36 |
col->width--; |
|
247 |
1638 |
ewidth += col->width; |
|
248 |
1638 |
} |
|
249 |
} |
||
250 |
13167 |
} |
|
251 |
|||
252 |
static void |
||
253 |
tblcalc_data(struct rofftbl *tbl, struct roffcol *col, |
||
254 |
const struct tbl_opts *opts, const struct tbl_dat *dp, size_t mw) |
||
255 |
{ |
||
256 |
size_t sz; |
||
257 |
|||
258 |
/* Branch down into data sub-types. */ |
||
259 |
|||
260 |
✗✓✗✗ ✗✓✓✗ ✓ |
160461 |
switch (dp->layout->pos) { |
261 |
case TBL_CELL_HORIZ: |
||
262 |
case TBL_CELL_DHORIZ: |
||
263 |
4104 |
sz = (*tbl->len)(1, tbl->arg); |
|
264 |
✓✓ | 4104 |
if (col->width < sz) |
265 |
1260 |
col->width = sz; |
|
266 |
break; |
||
267 |
case TBL_CELL_LONG: |
||
268 |
case TBL_CELL_CENTRE: |
||
269 |
case TBL_CELL_LEFT: |
||
270 |
case TBL_CELL_RIGHT: |
||
271 |
49284 |
tblcalc_literal(tbl, col, dp, mw); |
|
272 |
49284 |
break; |
|
273 |
case TBL_CELL_NUMBER: |
||
274 |
81 |
tblcalc_number(tbl, col, opts, dp); |
|
275 |
81 |
break; |
|
276 |
case TBL_CELL_DOWN: |
||
277 |
break; |
||
278 |
default: |
||
279 |
abort(); |
||
280 |
} |
||
281 |
53496 |
} |
|
282 |
|||
283 |
static void |
||
284 |
tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, |
||
285 |
const struct tbl_dat *dp, size_t mw) |
||
286 |
{ |
||
287 |
const char *str; /* Beginning of the first line. */ |
||
288 |
const char *beg; /* Beginning of the current line. */ |
||
289 |
char *end; /* End of the current line. */ |
||
290 |
size_t lsz; /* Length of the current line. */ |
||
291 |
size_t wsz; /* Length of the current word. */ |
||
292 |
|||
293 |
✓✗✓✓ |
147852 |
if (dp->string == NULL || *dp->string == '\0') |
294 |
72 |
return; |
|
295 |
✓✓ | 98685 |
str = mw ? mandoc_strdup(dp->string) : dp->string; |
296 |
lsz = 0; |
||
297 |
✓✓✓✓ |
345780 |
for (beg = str; beg != NULL && *beg != '\0'; beg = end) { |
298 |
✓✓ | 99657 |
end = mw ? strchr(beg, ' ') : NULL; |
299 |
✓✓ | 49536 |
if (end != NULL) { |
300 |
324 |
*end++ = '\0'; |
|
301 |
✗✓ | 648 |
while (*end == ' ') |
302 |
end++; |
||
303 |
} |
||
304 |
49536 |
wsz = (*tbl->slen)(beg, tbl->arg); |
|
305 |
✓✓✓✓ |
49860 |
if (mw && lsz && lsz + 1 + wsz <= mw) |
306 |
189 |
lsz += 1 + wsz; |
|
307 |
else |
||
308 |
lsz = wsz; |
||
309 |
✓✓ | 49536 |
if (col->width < lsz) |
310 |
18054 |
col->width = lsz; |
|
311 |
} |
||
312 |
✓✓ | 49212 |
if (mw) |
313 |
261 |
free((void *)str); |
|
314 |
98496 |
} |
|
315 |
|||
316 |
static void |
||
317 |
tblcalc_number(struct rofftbl *tbl, struct roffcol *col, |
||
318 |
const struct tbl_opts *opts, const struct tbl_dat *dp) |
||
319 |
{ |
||
320 |
int i; |
||
321 |
size_t sz, psz, ssz, d; |
||
322 |
const char *str; |
||
323 |
char *cp; |
||
324 |
162 |
char buf[2]; |
|
325 |
|||
326 |
/* |
||
327 |
* First calculate number width and decimal place (last + 1 for |
||
328 |
* non-decimal numbers). If the stored decimal is subsequent to |
||
329 |
* ours, make our size longer by that difference |
||
330 |
* (right-"shifting"); similarly, if ours is subsequent the |
||
331 |
* stored, then extend the stored size by the difference. |
||
332 |
* Finally, re-assign the stored values. |
||
333 |
*/ |
||
334 |
|||
335 |
✓✗ | 243 |
str = dp->string ? dp->string : ""; |
336 |
81 |
sz = (*tbl->slen)(str, tbl->arg); |
|
337 |
|||
338 |
/* FIXME: TBL_DATA_HORIZ et al.? */ |
||
339 |
|||
340 |
81 |
buf[0] = opts->decimal; |
|
341 |
81 |
buf[1] = '\0'; |
|
342 |
|||
343 |
81 |
psz = (*tbl->slen)(buf, tbl->arg); |
|
344 |
|||
345 |
✓✗ | 81 |
if (NULL != (cp = strrchr(str, opts->decimal))) { |
346 |
81 |
buf[1] = '\0'; |
|
347 |
✓✓ | 450 |
for (ssz = 0, i = 0; cp != &str[i]; i++) { |
348 |
144 |
buf[0] = str[i]; |
|
349 |
144 |
ssz += (*tbl->slen)(buf, tbl->arg); |
|
350 |
} |
||
351 |
81 |
d = ssz + psz; |
|
352 |
81 |
} else |
|
353 |
d = sz + psz; |
||
354 |
|||
355 |
/* Adjust the settings for this column. */ |
||
356 |
|||
357 |
✓✓ | 81 |
if (col->decimal > d) { |
358 |
18 |
sz += col->decimal - d; |
|
359 |
d = col->decimal; |
||
360 |
18 |
} else |
|
361 |
63 |
col->width += d - col->decimal; |
|
362 |
|||
363 |
✓✓ | 81 |
if (sz > col->width) |
364 |
63 |
col->width = sz; |
|
365 |
✓✓ | 81 |
if (d > col->decimal) |
366 |
63 |
col->decimal = d; |
|
367 |
81 |
} |
Generated by: GCOVR (Version 3.3) |