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