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 |
24936 |
char *endptr; |
|
50 |
|||
51 |
12468 |
dst->unit = def == SCALE_MAX ? SCALE_BU : def; |
|
52 |
12468 |
dst->scale = strtod(src, &endptr); |
|
53 |
✓✓ | 12468 |
if (endptr == src) |
54 |
321 |
return NULL; |
|
55 |
|||
56 |
✓✓✓✓ ✓✓✓✓ ✓✓✓ |
12147 |
switch (*endptr++) { |
57 |
case 'c': |
||
58 |
dst->unit = SCALE_CM; |
||
59 |
15 |
break; |
|
60 |
case 'i': |
||
61 |
dst->unit = SCALE_IN; |
||
62 |
6720 |
break; |
|
63 |
case 'f': |
||
64 |
dst->unit = SCALE_FS; |
||
65 |
3 |
break; |
|
66 |
case 'M': |
||
67 |
dst->unit = SCALE_MM; |
||
68 |
15 |
break; |
|
69 |
case 'm': |
||
70 |
dst->unit = SCALE_EM; |
||
71 |
15 |
break; |
|
72 |
case 'n': |
||
73 |
dst->unit = SCALE_EN; |
||
74 |
4260 |
break; |
|
75 |
case 'P': |
||
76 |
dst->unit = SCALE_PC; |
||
77 |
15 |
break; |
|
78 |
case 'p': |
||
79 |
dst->unit = SCALE_PT; |
||
80 |
18 |
break; |
|
81 |
case 'u': |
||
82 |
dst->unit = SCALE_BU; |
||
83 |
15 |
break; |
|
84 |
case 'v': |
||
85 |
dst->unit = SCALE_VS; |
||
86 |
822 |
break; |
|
87 |
default: |
||
88 |
249 |
endptr--; |
|
89 |
✓✓ | 249 |
if (SCALE_MAX == def) |
90 |
6 |
return NULL; |
|
91 |
dst->unit = def; |
||
92 |
243 |
break; |
|
93 |
} |
||
94 |
12141 |
return endptr; |
|
95 |
12468 |
} |
|
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 |
4408 |
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 |
✗✓ | 2204 |
assert(NULL == tbl->cols); |
122 |
2204 |
tbl->cols = mandoc_calloc((size_t)sp->opts->cols, |
|
123 |
sizeof(struct roffcol)); |
||
124 |
2204 |
opts = sp->opts; |
|
125 |
|||
126 |
✓✓ | 18742 |
for (maxcol = -1; sp; sp = sp->next) { |
127 |
✓✓ | 7167 |
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 |
✓✓ | 50550 |
for (dp = sp->first; dp; dp = dp->next) { |
135 |
/* Do not used spanned cells in the calculation. */ |
||
136 |
✓✓ | 18186 |
if (0 < --spans) |
137 |
continue; |
||
138 |
18183 |
spans = dp->spans; |
|
139 |
✓✓ | 18183 |
if (1 < spans) |
140 |
continue; |
||
141 |
18180 |
icol = dp->layout->col; |
|
142 |
✓✓ | 48070 |
while (maxcol < icol) |
143 |
5855 |
tbl->cols[++maxcol].spacing = SIZE_MAX; |
|
144 |
18180 |
col = tbl->cols + icol; |
|
145 |
18180 |
col->flags |= dp->layout->flags; |
|
146 |
✓✓ | 18180 |
if (dp->layout->flags & TBL_CELL_WIGN) |
147 |
continue; |
||
148 |
✓✓✓✗ |
18186 |
if (dp->layout->wstr != NULL && |
149 |
✓✓ | 24 |
dp->layout->width == 0 && |
150 |
18 |
a2roffsu(dp->layout->wstr, &su, SCALE_EN) |
|
151 |
18 |
!= NULL) |
|
152 |
18 |
dp->layout->width = |
|
153 |
18 |
(*tbl->sulen)(&su, tbl->arg); |
|
154 |
✓✓ | 18168 |
if (col->width < dp->layout->width) |
155 |
18 |
col->width = dp->layout->width; |
|
156 |
✓✓✗✓ |
18222 |
if (dp->layout->spacing != SIZE_MAX && |
157 |
✓✓ | 84 |
(col->spacing == SIZE_MAX || |
158 |
54 |
col->spacing < dp->layout->spacing)) |
|
159 |
30 |
col->spacing = dp->layout->spacing; |
|
160 |
18168 |
tblcalc_data(tbl, col, opts, dp, |
|
161 |
✓✓ | 36336 |
dp->block == 0 ? 0 : |
162 |
✓✓ | 99 |
dp->layout->width ? dp->layout->width : |
163 |
✓✗ | 225 |
rmargin ? (rmargin + sp->opts->cols / 2) |
164 |
75 |
/ (sp->opts->cols + 1) : 0); |
|
165 |
18168 |
} |
|
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 |
✓✓ | 16118 |
for (icol = 0; icol <= maxcol; icol++) { |
177 |
5855 |
col = tbl->cols + icol; |
|
178 |
✓✓✓✓ |
5885 |
if (col->spacing == SIZE_MAX || icol == maxcol) |
179 |
5831 |
col->spacing = 3; |
|
180 |
✗✓ | 5855 |
if (col->flags & TBL_CELL_EQUAL) { |
181 |
necol++; |
||
182 |
if (ewidth < col->width) |
||
183 |
ewidth = col->width; |
||
184 |
} |
||
185 |
✓✓ | 5855 |
if (col->flags & TBL_CELL_WMAX) |
186 |
552 |
nxcol++; |
|
187 |
else |
||
188 |
5303 |
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 |
✗✓ | 2204 |
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 |
✓✓ | 2204 |
if (nxcol && rmargin) { |
216 |
270 |
xwidth += 3*maxcol + |
|
217 |
✓✗ | 405 |
(opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ? |
218 |
135 |
2 : !!opts->lvert + !!opts->rvert); |
|
219 |
✓✓ | 135 |
if (rmargin <= offset + xwidth) |
220 |
3 |
return; |
|
221 |
132 |
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 |
✓✓ | 132 |
if (nxcol == 5) { |
231 |
30 |
quirkcol = xwidth % nxcol + 2; |
|
232 |
30 |
if (quirkcol != 3 && quirkcol != 4) |
|
233 |
quirkcol = -1; |
||
234 |
} else |
||
235 |
quirkcol = -1; |
||
236 |
|||
237 |
necol = 0; |
||
238 |
ewidth = 0; |
||
239 |
✓✓ | 1620 |
for (icol = 0; icol <= maxcol; icol++) { |
240 |
678 |
col = tbl->cols + icol; |
|
241 |
✓✓ | 678 |
if ( ! (col->flags & TBL_CELL_WMAX)) |
242 |
continue; |
||
243 |
1092 |
col->width = (double)xwidth * ++necol / nxcol |
|
244 |
546 |
- ewidth + 0.4995; |
|
245 |
✓✓ | 546 |
if (necol == quirkcol) |
246 |
12 |
col->width--; |
|
247 |
546 |
ewidth += col->width; |
|
248 |
546 |
} |
|
249 |
} |
||
250 |
4405 |
} |
|
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 |
✗✓✗✗ ✗✓✓✗ ✓ |
54495 |
switch (dp->layout->pos) { |
261 |
case TBL_CELL_HORIZ: |
||
262 |
case TBL_CELL_DHORIZ: |
||
263 |
1368 |
sz = (*tbl->len)(1, tbl->arg); |
|
264 |
✓✓ | 1368 |
if (col->width < sz) |
265 |
420 |
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 |
16764 |
tblcalc_literal(tbl, col, dp, mw); |
|
272 |
16764 |
break; |
|
273 |
case TBL_CELL_NUMBER: |
||
274 |
27 |
tblcalc_number(tbl, col, opts, dp); |
|
275 |
27 |
break; |
|
276 |
case TBL_CELL_DOWN: |
||
277 |
break; |
||
278 |
default: |
||
279 |
abort(); |
||
280 |
} |
||
281 |
18168 |
} |
|
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 |
✓✗✓✓ |
50292 |
if (dp->string == NULL || *dp->string == '\0') |
294 |
32 |
return; |
|
295 |
✓✓ | 33551 |
str = mw ? mandoc_strdup(dp->string) : dp->string; |
296 |
lsz = 0; |
||
297 |
✓✓✓✓ |
117556 |
for (beg = str; beg != NULL && *beg != '\0'; beg = end) { |
298 |
✓✓ | 33875 |
end = mw ? strchr(beg, ' ') : NULL; |
299 |
✓✓ | 16840 |
if (end != NULL) { |
300 |
108 |
*end++ = '\0'; |
|
301 |
✗✓ | 216 |
while (*end == ' ') |
302 |
end++; |
||
303 |
} |
||
304 |
16840 |
wsz = (*tbl->slen)(beg, tbl->arg); |
|
305 |
✓✓✓✓ |
16948 |
if (mw && lsz && lsz + 1 + wsz <= mw) |
306 |
63 |
lsz += 1 + wsz; |
|
307 |
else |
||
308 |
lsz = wsz; |
||
309 |
✓✓ | 16840 |
if (col->width < lsz) |
310 |
6086 |
col->width = lsz; |
|
311 |
} |
||
312 |
✓✓ | 16732 |
if (mw) |
313 |
87 |
free((void *)str); |
|
314 |
33496 |
} |
|
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 |
54 |
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 |
✓✗ | 81 |
str = dp->string ? dp->string : ""; |
336 |
27 |
sz = (*tbl->slen)(str, tbl->arg); |
|
337 |
|||
338 |
/* FIXME: TBL_DATA_HORIZ et al.? */ |
||
339 |
|||
340 |
27 |
buf[0] = opts->decimal; |
|
341 |
27 |
buf[1] = '\0'; |
|
342 |
|||
343 |
27 |
psz = (*tbl->slen)(buf, tbl->arg); |
|
344 |
|||
345 |
✓✗ | 27 |
if (NULL != (cp = strrchr(str, opts->decimal))) { |
346 |
27 |
buf[1] = '\0'; |
|
347 |
✓✓ | 150 |
for (ssz = 0, i = 0; cp != &str[i]; i++) { |
348 |
48 |
buf[0] = str[i]; |
|
349 |
48 |
ssz += (*tbl->slen)(buf, tbl->arg); |
|
350 |
} |
||
351 |
27 |
d = ssz + psz; |
|
352 |
27 |
} else |
|
353 |
d = sz + psz; |
||
354 |
|||
355 |
/* Adjust the settings for this column. */ |
||
356 |
|||
357 |
✓✓ | 27 |
if (col->decimal > d) { |
358 |
6 |
sz += col->decimal - d; |
|
359 |
d = col->decimal; |
||
360 |
6 |
} else |
|
361 |
21 |
col->width += d - col->decimal; |
|
362 |
|||
363 |
✓✓ | 27 |
if (sz > col->width) |
364 |
21 |
col->width = sz; |
|
365 |
✓✓ | 27 |
if (d > col->decimal) |
366 |
21 |
col->decimal = d; |
|
367 |
27 |
} |
Generated by: GCOVR (Version 3.3) |