1 /* $Id: out.c,v 1.49 2014/08/01 19:25:52 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2011 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 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include <sys/types.h> 23 24 #include <assert.h> 25 #include <ctype.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <time.h> 30 31 #include "mandoc_aux.h" 32 #include "mandoc.h" 33 #include "out.h" 34 35 static void tblcalc_data(struct rofftbl *, struct roffcol *, 36 const struct tbl_opts *, const struct tbl_dat *); 37 static void tblcalc_literal(struct rofftbl *, struct roffcol *, 38 const struct tbl_dat *); 39 static void tblcalc_number(struct rofftbl *, struct roffcol *, 40 const struct tbl_opts *, const struct tbl_dat *); 41 42 43 /* 44 * Convert a `scaling unit' to a consistent form, or fail. Scaling 45 * units are documented in groff.7, mdoc.7, man.7. 46 */ 47 int 48 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) 49 { 50 char buf[BUFSIZ], hasd; 51 int i; 52 enum roffscale unit; 53 54 if ('\0' == *src) 55 return(0); 56 57 i = hasd = 0; 58 59 switch (*src) { 60 case '+': 61 src++; 62 break; 63 case '-': 64 buf[i++] = *src++; 65 break; 66 default: 67 break; 68 } 69 70 if ('\0' == *src) 71 return(0); 72 73 while (i < BUFSIZ) { 74 if ( ! isdigit((unsigned char)*src)) { 75 if ('.' != *src) 76 break; 77 else if (hasd) 78 break; 79 else 80 hasd = 1; 81 } 82 buf[i++] = *src++; 83 } 84 85 if (BUFSIZ == i || (*src && *(src + 1))) 86 return(0); 87 88 buf[i] = '\0'; 89 90 switch (*src) { 91 case 'c': 92 unit = SCALE_CM; 93 break; 94 case 'i': 95 unit = SCALE_IN; 96 break; 97 case 'P': 98 unit = SCALE_PC; 99 break; 100 case 'p': 101 unit = SCALE_PT; 102 break; 103 case 'f': 104 unit = SCALE_FS; 105 break; 106 case 'v': 107 unit = SCALE_VS; 108 break; 109 case 'm': 110 unit = SCALE_EM; 111 break; 112 case '\0': 113 if (SCALE_MAX == def) 114 return(0); 115 unit = SCALE_BU; 116 break; 117 case 'u': 118 unit = SCALE_BU; 119 break; 120 case 'M': 121 unit = SCALE_MM; 122 break; 123 case 'n': 124 unit = SCALE_EN; 125 break; 126 default: 127 return(0); 128 } 129 130 /* FIXME: do this in the caller. */ 131 if ((dst->scale = atof(buf)) < 0.0) 132 dst->scale = 0.0; 133 dst->unit = unit; 134 return(1); 135 } 136 137 /* 138 * Calculate the abstract widths and decimal positions of columns in a 139 * table. This routine allocates the columns structures then runs over 140 * all rows and cells in the table. The function pointers in "tbl" are 141 * used for the actual width calculations. 142 */ 143 void 144 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp) 145 { 146 const struct tbl_dat *dp; 147 struct roffcol *col; 148 int spans; 149 150 /* 151 * Allocate the master column specifiers. These will hold the 152 * widths and decimal positions for all cells in the column. It 153 * must be freed and nullified by the caller. 154 */ 155 156 assert(NULL == tbl->cols); 157 tbl->cols = mandoc_calloc((size_t)sp->opts->cols, 158 sizeof(struct roffcol)); 159 160 for ( ; sp; sp = sp->next) { 161 if (TBL_SPAN_DATA != sp->pos) 162 continue; 163 spans = 1; 164 /* 165 * Account for the data cells in the layout, matching it 166 * to data cells in the data section. 167 */ 168 for (dp = sp->first; dp; dp = dp->next) { 169 /* Do not used spanned cells in the calculation. */ 170 if (0 < --spans) 171 continue; 172 spans = dp->spans; 173 if (1 < spans) 174 continue; 175 assert(dp->layout); 176 col = &tbl->cols[dp->layout->head->ident]; 177 tblcalc_data(tbl, col, sp->opts, dp); 178 } 179 } 180 } 181 182 static void 183 tblcalc_data(struct rofftbl *tbl, struct roffcol *col, 184 const struct tbl_opts *opts, const struct tbl_dat *dp) 185 { 186 size_t sz; 187 188 /* Branch down into data sub-types. */ 189 190 switch (dp->layout->pos) { 191 case TBL_CELL_HORIZ: 192 /* FALLTHROUGH */ 193 case TBL_CELL_DHORIZ: 194 sz = (*tbl->len)(1, tbl->arg); 195 if (col->width < sz) 196 col->width = sz; 197 break; 198 case TBL_CELL_LONG: 199 /* FALLTHROUGH */ 200 case TBL_CELL_CENTRE: 201 /* FALLTHROUGH */ 202 case TBL_CELL_LEFT: 203 /* FALLTHROUGH */ 204 case TBL_CELL_RIGHT: 205 tblcalc_literal(tbl, col, dp); 206 break; 207 case TBL_CELL_NUMBER: 208 tblcalc_number(tbl, col, opts, dp); 209 break; 210 case TBL_CELL_DOWN: 211 break; 212 default: 213 abort(); 214 /* NOTREACHED */ 215 } 216 } 217 218 static void 219 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, 220 const struct tbl_dat *dp) 221 { 222 size_t sz; 223 const char *str; 224 225 str = dp->string ? dp->string : ""; 226 sz = (*tbl->slen)(str, tbl->arg); 227 228 if (col->width < sz) 229 col->width = sz; 230 } 231 232 static void 233 tblcalc_number(struct rofftbl *tbl, struct roffcol *col, 234 const struct tbl_opts *opts, const struct tbl_dat *dp) 235 { 236 int i; 237 size_t sz, psz, ssz, d; 238 const char *str; 239 char *cp; 240 char buf[2]; 241 242 /* 243 * First calculate number width and decimal place (last + 1 for 244 * non-decimal numbers). If the stored decimal is subsequent to 245 * ours, make our size longer by that difference 246 * (right-"shifting"); similarly, if ours is subsequent the 247 * stored, then extend the stored size by the difference. 248 * Finally, re-assign the stored values. 249 */ 250 251 str = dp->string ? dp->string : ""; 252 sz = (*tbl->slen)(str, tbl->arg); 253 254 /* FIXME: TBL_DATA_HORIZ et al.? */ 255 256 buf[0] = opts->decimal; 257 buf[1] = '\0'; 258 259 psz = (*tbl->slen)(buf, tbl->arg); 260 261 if (NULL != (cp = strrchr(str, opts->decimal))) { 262 buf[1] = '\0'; 263 for (ssz = 0, i = 0; cp != &str[i]; i++) { 264 buf[0] = str[i]; 265 ssz += (*tbl->slen)(buf, tbl->arg); 266 } 267 d = ssz + psz; 268 } else 269 d = sz + psz; 270 271 /* Adjust the settings for this column. */ 272 273 if (col->decimal > d) { 274 sz += col->decimal - d; 275 d = col->decimal; 276 } else 277 col->width += d - col->decimal; 278 279 if (sz > col->width) 280 col->width = sz; 281 if (d > col->decimal) 282 col->decimal = d; 283 } 284