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