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