1*260e9a87SYuri Pankov /* $Id: out.c,v 1.59 2015/01/30 04:11:50 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 395c635efSGarrett D'Amore * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4*260e9a87SYuri Pankov * Copyright (c) 2011, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> 595c635efSGarrett D'Amore * 695c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 795c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 895c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies. 995c635efSGarrett D'Amore * 1095c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1195c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1295c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1395c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1495c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1595c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1695c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1795c635efSGarrett D'Amore */ 1895c635efSGarrett D'Amore #include "config.h" 1995c635efSGarrett D'Amore 2095c635efSGarrett D'Amore #include <sys/types.h> 2195c635efSGarrett D'Amore 2295c635efSGarrett D'Amore #include <assert.h> 2395c635efSGarrett D'Amore #include <stdlib.h> 2495c635efSGarrett D'Amore #include <string.h> 2595c635efSGarrett D'Amore #include <time.h> 2695c635efSGarrett D'Amore 27*260e9a87SYuri Pankov #include "mandoc_aux.h" 2895c635efSGarrett D'Amore #include "mandoc.h" 2995c635efSGarrett D'Amore #include "out.h" 3095c635efSGarrett D'Amore 3195c635efSGarrett D'Amore static void tblcalc_data(struct rofftbl *, struct roffcol *, 32698f87a4SGarrett D'Amore const struct tbl_opts *, const struct tbl_dat *); 3395c635efSGarrett D'Amore static void tblcalc_literal(struct rofftbl *, struct roffcol *, 3495c635efSGarrett D'Amore const struct tbl_dat *); 3595c635efSGarrett D'Amore static void tblcalc_number(struct rofftbl *, struct roffcol *, 36698f87a4SGarrett D'Amore const struct tbl_opts *, const struct tbl_dat *); 3795c635efSGarrett D'Amore 38*260e9a87SYuri Pankov 3995c635efSGarrett D'Amore /* 40*260e9a87SYuri Pankov * Parse the *src string and store a scaling unit into *dst. 41*260e9a87SYuri Pankov * If the string doesn't specify the unit, use the default. 42*260e9a87SYuri Pankov * If no default is specified, fail. 43*260e9a87SYuri Pankov * Return 2 on complete success, 1 when a conversion was done, 44*260e9a87SYuri Pankov * but there was trailing garbage, and 0 on total failure. 4595c635efSGarrett D'Amore */ 4695c635efSGarrett D'Amore int 4795c635efSGarrett D'Amore a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) 4895c635efSGarrett D'Amore { 49*260e9a87SYuri Pankov char *endptr; 5095c635efSGarrett D'Amore 51*260e9a87SYuri Pankov dst->unit = def == SCALE_MAX ? SCALE_BU : def; 52*260e9a87SYuri Pankov dst->scale = strtod(src, &endptr); 53*260e9a87SYuri Pankov if (endptr == src) 5495c635efSGarrett D'Amore return(0); 5595c635efSGarrett D'Amore 56*260e9a87SYuri Pankov switch (*endptr++) { 57*260e9a87SYuri Pankov case 'c': 58*260e9a87SYuri Pankov dst->unit = SCALE_CM; 5995c635efSGarrett D'Amore break; 60*260e9a87SYuri Pankov case 'i': 61*260e9a87SYuri Pankov dst->unit = SCALE_IN; 6295c635efSGarrett D'Amore break; 63*260e9a87SYuri Pankov case 'f': 64*260e9a87SYuri Pankov dst->unit = SCALE_FS; 65*260e9a87SYuri Pankov break; 66*260e9a87SYuri Pankov case 'M': 67*260e9a87SYuri Pankov dst->unit = SCALE_MM; 68*260e9a87SYuri Pankov break; 69*260e9a87SYuri Pankov case 'm': 70*260e9a87SYuri Pankov dst->unit = SCALE_EM; 71*260e9a87SYuri Pankov break; 72*260e9a87SYuri Pankov case 'n': 73*260e9a87SYuri Pankov dst->unit = SCALE_EN; 74*260e9a87SYuri Pankov break; 75*260e9a87SYuri Pankov case 'P': 76*260e9a87SYuri Pankov dst->unit = SCALE_PC; 77*260e9a87SYuri Pankov break; 78*260e9a87SYuri Pankov case 'p': 79*260e9a87SYuri Pankov dst->unit = SCALE_PT; 80*260e9a87SYuri Pankov break; 81*260e9a87SYuri Pankov case 'u': 82*260e9a87SYuri Pankov dst->unit = SCALE_BU; 83*260e9a87SYuri Pankov break; 84*260e9a87SYuri Pankov case 'v': 85*260e9a87SYuri Pankov dst->unit = SCALE_VS; 86*260e9a87SYuri Pankov break; 87*260e9a87SYuri Pankov case '\0': 88*260e9a87SYuri Pankov endptr--; 89*260e9a87SYuri Pankov /* FALLTHROUGH */ 9095c635efSGarrett D'Amore default: 9195c635efSGarrett D'Amore if (SCALE_MAX == def) 9295c635efSGarrett D'Amore return(0); 93*260e9a87SYuri Pankov dst->unit = def; 9495c635efSGarrett D'Amore break; 9595c635efSGarrett D'Amore } 9695c635efSGarrett D'Amore 97*260e9a87SYuri Pankov return(*endptr == '\0' ? 2 : 1); 9895c635efSGarrett D'Amore } 9995c635efSGarrett D'Amore 10095c635efSGarrett D'Amore /* 10195c635efSGarrett D'Amore * Calculate the abstract widths and decimal positions of columns in a 10295c635efSGarrett D'Amore * table. This routine allocates the columns structures then runs over 10395c635efSGarrett D'Amore * all rows and cells in the table. The function pointers in "tbl" are 10495c635efSGarrett D'Amore * used for the actual width calculations. 10595c635efSGarrett D'Amore */ 10695c635efSGarrett D'Amore void 107*260e9a87SYuri Pankov tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, 108*260e9a87SYuri Pankov size_t totalwidth) 10995c635efSGarrett D'Amore { 110*260e9a87SYuri Pankov const struct tbl_opts *opts; 11195c635efSGarrett D'Amore const struct tbl_dat *dp; 11295c635efSGarrett D'Amore struct roffcol *col; 113*260e9a87SYuri Pankov size_t ewidth, xwidth; 11495c635efSGarrett D'Amore int spans; 115*260e9a87SYuri Pankov int icol, maxcol, necol, nxcol, quirkcol; 11695c635efSGarrett D'Amore 11795c635efSGarrett D'Amore /* 11895c635efSGarrett D'Amore * Allocate the master column specifiers. These will hold the 11995c635efSGarrett D'Amore * widths and decimal positions for all cells in the column. It 12095c635efSGarrett D'Amore * must be freed and nullified by the caller. 12195c635efSGarrett D'Amore */ 12295c635efSGarrett D'Amore 12395c635efSGarrett D'Amore assert(NULL == tbl->cols); 124*260e9a87SYuri Pankov tbl->cols = mandoc_calloc((size_t)sp->opts->cols, 125*260e9a87SYuri Pankov sizeof(struct roffcol)); 126*260e9a87SYuri Pankov opts = sp->opts; 12795c635efSGarrett D'Amore 128*260e9a87SYuri Pankov for (maxcol = -1; sp; sp = sp->next) { 12995c635efSGarrett D'Amore if (TBL_SPAN_DATA != sp->pos) 13095c635efSGarrett D'Amore continue; 13195c635efSGarrett D'Amore spans = 1; 13295c635efSGarrett D'Amore /* 13395c635efSGarrett D'Amore * Account for the data cells in the layout, matching it 13495c635efSGarrett D'Amore * to data cells in the data section. 13595c635efSGarrett D'Amore */ 13695c635efSGarrett D'Amore for (dp = sp->first; dp; dp = dp->next) { 13795c635efSGarrett D'Amore /* Do not used spanned cells in the calculation. */ 13895c635efSGarrett D'Amore if (0 < --spans) 13995c635efSGarrett D'Amore continue; 14095c635efSGarrett D'Amore spans = dp->spans; 14195c635efSGarrett D'Amore if (1 < spans) 14295c635efSGarrett D'Amore continue; 143*260e9a87SYuri Pankov icol = dp->layout->col; 144*260e9a87SYuri Pankov if (maxcol < icol) 145*260e9a87SYuri Pankov maxcol = icol; 146*260e9a87SYuri Pankov col = tbl->cols + icol; 147*260e9a87SYuri Pankov col->flags |= dp->layout->flags; 148*260e9a87SYuri Pankov if (dp->layout->flags & TBL_CELL_WIGN) 149*260e9a87SYuri Pankov continue; 150*260e9a87SYuri Pankov tblcalc_data(tbl, col, opts, dp); 151*260e9a87SYuri Pankov } 152*260e9a87SYuri Pankov } 153*260e9a87SYuri Pankov 154*260e9a87SYuri Pankov /* 155*260e9a87SYuri Pankov * Count columns to equalize and columns to maximize. 156*260e9a87SYuri Pankov * Find maximum width of the columns to equalize. 157*260e9a87SYuri Pankov * Find total width of the columns *not* to maximize. 158*260e9a87SYuri Pankov */ 159*260e9a87SYuri Pankov 160*260e9a87SYuri Pankov necol = nxcol = 0; 161*260e9a87SYuri Pankov ewidth = xwidth = 0; 162*260e9a87SYuri Pankov for (icol = 0; icol <= maxcol; icol++) { 163*260e9a87SYuri Pankov col = tbl->cols + icol; 164*260e9a87SYuri Pankov if (col->flags & TBL_CELL_EQUAL) { 165*260e9a87SYuri Pankov necol++; 166*260e9a87SYuri Pankov if (ewidth < col->width) 167*260e9a87SYuri Pankov ewidth = col->width; 168*260e9a87SYuri Pankov } 169*260e9a87SYuri Pankov if (col->flags & TBL_CELL_WMAX) 170*260e9a87SYuri Pankov nxcol++; 171*260e9a87SYuri Pankov else 172*260e9a87SYuri Pankov xwidth += col->width; 173*260e9a87SYuri Pankov } 174*260e9a87SYuri Pankov 175*260e9a87SYuri Pankov /* 176*260e9a87SYuri Pankov * Equalize columns, if requested for any of them. 177*260e9a87SYuri Pankov * Update total width of the columns not to maximize. 178*260e9a87SYuri Pankov */ 179*260e9a87SYuri Pankov 180*260e9a87SYuri Pankov if (necol) { 181*260e9a87SYuri Pankov for (icol = 0; icol <= maxcol; icol++) { 182*260e9a87SYuri Pankov col = tbl->cols + icol; 183*260e9a87SYuri Pankov if ( ! (col->flags & TBL_CELL_EQUAL)) 184*260e9a87SYuri Pankov continue; 185*260e9a87SYuri Pankov if (col->width == ewidth) 186*260e9a87SYuri Pankov continue; 187*260e9a87SYuri Pankov if (nxcol && totalwidth) 188*260e9a87SYuri Pankov xwidth += ewidth - col->width; 189*260e9a87SYuri Pankov col->width = ewidth; 190*260e9a87SYuri Pankov } 191*260e9a87SYuri Pankov } 192*260e9a87SYuri Pankov 193*260e9a87SYuri Pankov /* 194*260e9a87SYuri Pankov * If there are any columns to maximize, find the total 195*260e9a87SYuri Pankov * available width, deducting 3n margins between columns. 196*260e9a87SYuri Pankov * Distribute the available width evenly. 197*260e9a87SYuri Pankov */ 198*260e9a87SYuri Pankov 199*260e9a87SYuri Pankov if (nxcol && totalwidth) { 200*260e9a87SYuri Pankov xwidth = totalwidth - xwidth - 3*maxcol - 201*260e9a87SYuri Pankov (opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ? 202*260e9a87SYuri Pankov 2 : !!opts->lvert + !!opts->rvert); 203*260e9a87SYuri Pankov 204*260e9a87SYuri Pankov /* 205*260e9a87SYuri Pankov * Emulate a bug in GNU tbl width calculation that 206*260e9a87SYuri Pankov * manifests itself for large numbers of x-columns. 207*260e9a87SYuri Pankov * Emulating it for 5 x-columns gives identical 208*260e9a87SYuri Pankov * behaviour for up to 6 x-columns. 209*260e9a87SYuri Pankov */ 210*260e9a87SYuri Pankov 211*260e9a87SYuri Pankov if (nxcol == 5) { 212*260e9a87SYuri Pankov quirkcol = xwidth % nxcol + 2; 213*260e9a87SYuri Pankov if (quirkcol != 3 && quirkcol != 4) 214*260e9a87SYuri Pankov quirkcol = -1; 215*260e9a87SYuri Pankov } else 216*260e9a87SYuri Pankov quirkcol = -1; 217*260e9a87SYuri Pankov 218*260e9a87SYuri Pankov necol = 0; 219*260e9a87SYuri Pankov ewidth = 0; 220*260e9a87SYuri Pankov for (icol = 0; icol <= maxcol; icol++) { 221*260e9a87SYuri Pankov col = tbl->cols + icol; 222*260e9a87SYuri Pankov if ( ! (col->flags & TBL_CELL_WMAX)) 223*260e9a87SYuri Pankov continue; 224*260e9a87SYuri Pankov col->width = (double)xwidth * ++necol / nxcol 225*260e9a87SYuri Pankov - ewidth + 0.4995; 226*260e9a87SYuri Pankov if (necol == quirkcol) 227*260e9a87SYuri Pankov col->width--; 228*260e9a87SYuri Pankov ewidth += col->width; 22995c635efSGarrett D'Amore } 23095c635efSGarrett D'Amore } 23195c635efSGarrett D'Amore } 23295c635efSGarrett D'Amore 23395c635efSGarrett D'Amore static void 23495c635efSGarrett D'Amore tblcalc_data(struct rofftbl *tbl, struct roffcol *col, 235698f87a4SGarrett D'Amore const struct tbl_opts *opts, const struct tbl_dat *dp) 23695c635efSGarrett D'Amore { 23795c635efSGarrett D'Amore size_t sz; 23895c635efSGarrett D'Amore 23995c635efSGarrett D'Amore /* Branch down into data sub-types. */ 24095c635efSGarrett D'Amore 24195c635efSGarrett D'Amore switch (dp->layout->pos) { 242*260e9a87SYuri Pankov case TBL_CELL_HORIZ: 24395c635efSGarrett D'Amore /* FALLTHROUGH */ 244*260e9a87SYuri Pankov case TBL_CELL_DHORIZ: 24595c635efSGarrett D'Amore sz = (*tbl->len)(1, tbl->arg); 24695c635efSGarrett D'Amore if (col->width < sz) 24795c635efSGarrett D'Amore col->width = sz; 24895c635efSGarrett D'Amore break; 249*260e9a87SYuri Pankov case TBL_CELL_LONG: 25095c635efSGarrett D'Amore /* FALLTHROUGH */ 251*260e9a87SYuri Pankov case TBL_CELL_CENTRE: 25295c635efSGarrett D'Amore /* FALLTHROUGH */ 253*260e9a87SYuri Pankov case TBL_CELL_LEFT: 25495c635efSGarrett D'Amore /* FALLTHROUGH */ 255*260e9a87SYuri Pankov case TBL_CELL_RIGHT: 25695c635efSGarrett D'Amore tblcalc_literal(tbl, col, dp); 25795c635efSGarrett D'Amore break; 258*260e9a87SYuri Pankov case TBL_CELL_NUMBER: 259698f87a4SGarrett D'Amore tblcalc_number(tbl, col, opts, dp); 26095c635efSGarrett D'Amore break; 261*260e9a87SYuri Pankov case TBL_CELL_DOWN: 26295c635efSGarrett D'Amore break; 26395c635efSGarrett D'Amore default: 26495c635efSGarrett D'Amore abort(); 26595c635efSGarrett D'Amore /* NOTREACHED */ 26695c635efSGarrett D'Amore } 26795c635efSGarrett D'Amore } 26895c635efSGarrett D'Amore 26995c635efSGarrett D'Amore static void 27095c635efSGarrett D'Amore tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, 27195c635efSGarrett D'Amore const struct tbl_dat *dp) 27295c635efSGarrett D'Amore { 27395c635efSGarrett D'Amore size_t sz; 27495c635efSGarrett D'Amore const char *str; 27595c635efSGarrett D'Amore 27695c635efSGarrett D'Amore str = dp->string ? dp->string : ""; 27795c635efSGarrett D'Amore sz = (*tbl->slen)(str, tbl->arg); 27895c635efSGarrett D'Amore 27995c635efSGarrett D'Amore if (col->width < sz) 28095c635efSGarrett D'Amore col->width = sz; 28195c635efSGarrett D'Amore } 28295c635efSGarrett D'Amore 28395c635efSGarrett D'Amore static void 28495c635efSGarrett D'Amore tblcalc_number(struct rofftbl *tbl, struct roffcol *col, 285698f87a4SGarrett D'Amore const struct tbl_opts *opts, const struct tbl_dat *dp) 28695c635efSGarrett D'Amore { 28795c635efSGarrett D'Amore int i; 28895c635efSGarrett D'Amore size_t sz, psz, ssz, d; 28995c635efSGarrett D'Amore const char *str; 29095c635efSGarrett D'Amore char *cp; 29195c635efSGarrett D'Amore char buf[2]; 29295c635efSGarrett D'Amore 29395c635efSGarrett D'Amore /* 29495c635efSGarrett D'Amore * First calculate number width and decimal place (last + 1 for 29595c635efSGarrett D'Amore * non-decimal numbers). If the stored decimal is subsequent to 29695c635efSGarrett D'Amore * ours, make our size longer by that difference 29795c635efSGarrett D'Amore * (right-"shifting"); similarly, if ours is subsequent the 29895c635efSGarrett D'Amore * stored, then extend the stored size by the difference. 29995c635efSGarrett D'Amore * Finally, re-assign the stored values. 30095c635efSGarrett D'Amore */ 30195c635efSGarrett D'Amore 30295c635efSGarrett D'Amore str = dp->string ? dp->string : ""; 30395c635efSGarrett D'Amore sz = (*tbl->slen)(str, tbl->arg); 30495c635efSGarrett D'Amore 30595c635efSGarrett D'Amore /* FIXME: TBL_DATA_HORIZ et al.? */ 30695c635efSGarrett D'Amore 307698f87a4SGarrett D'Amore buf[0] = opts->decimal; 30895c635efSGarrett D'Amore buf[1] = '\0'; 30995c635efSGarrett D'Amore 31095c635efSGarrett D'Amore psz = (*tbl->slen)(buf, tbl->arg); 31195c635efSGarrett D'Amore 312698f87a4SGarrett D'Amore if (NULL != (cp = strrchr(str, opts->decimal))) { 31395c635efSGarrett D'Amore buf[1] = '\0'; 31495c635efSGarrett D'Amore for (ssz = 0, i = 0; cp != &str[i]; i++) { 31595c635efSGarrett D'Amore buf[0] = str[i]; 31695c635efSGarrett D'Amore ssz += (*tbl->slen)(buf, tbl->arg); 31795c635efSGarrett D'Amore } 31895c635efSGarrett D'Amore d = ssz + psz; 31995c635efSGarrett D'Amore } else 32095c635efSGarrett D'Amore d = sz + psz; 32195c635efSGarrett D'Amore 32295c635efSGarrett D'Amore /* Adjust the settings for this column. */ 32395c635efSGarrett D'Amore 32495c635efSGarrett D'Amore if (col->decimal > d) { 32595c635efSGarrett D'Amore sz += col->decimal - d; 32695c635efSGarrett D'Amore d = col->decimal; 32795c635efSGarrett D'Amore } else 32895c635efSGarrett D'Amore col->width += d - col->decimal; 32995c635efSGarrett D'Amore 33095c635efSGarrett D'Amore if (sz > col->width) 33195c635efSGarrett D'Amore col->width = sz; 33295c635efSGarrett D'Amore if (d > col->decimal) 33395c635efSGarrett D'Amore col->decimal = d; 33495c635efSGarrett D'Amore } 335