| 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) |