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