1 /* $Id: out.c,v 1.40 2011/04/09 15:29:40 kristaps 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((u_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 /* 137 * Correctly writes the time in nroff form, which differs from standard 138 * form in that a space isn't printed in lieu of the extra %e field for 139 * single-digit dates. 140 */ 141 void 142 time2a(time_t t, char *dst, size_t sz) 143 { 144 struct tm tm; 145 char buf[5]; 146 char *p; 147 size_t nsz; 148 149 assert(sz > 1); 150 localtime_r(&t, &tm); 151 152 p = dst; 153 nsz = 0; 154 155 dst[0] = '\0'; 156 157 if (0 == (nsz = strftime(p, sz, "%B ", &tm))) 158 return; 159 160 p += (int)nsz; 161 sz -= nsz; 162 163 if (0 == strftime(buf, sizeof(buf), "%e, ", &tm)) 164 return; 165 166 nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz); 167 168 if (nsz >= sz) 169 return; 170 171 p += (int)nsz; 172 sz -= nsz; 173 174 (void)strftime(p, sz, "%Y", &tm); 175 } 176 177 /* 178 * Calculate the abstract widths and decimal positions of columns in a 179 * table. This routine allocates the columns structures then runs over 180 * all rows and cells in the table. The function pointers in "tbl" are 181 * used for the actual width calculations. 182 */ 183 void 184 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp) 185 { 186 const struct tbl_dat *dp; 187 const struct tbl_head *hp; 188 struct roffcol *col; 189 190 /* 191 * Allocate the master column specifiers. These will hold the 192 * widths and decimal positions for all cells in the column. It 193 * must be freed and nullified by the caller. 194 */ 195 196 assert(NULL == tbl->cols); 197 tbl->cols = mandoc_calloc 198 ((size_t)sp->tbl->cols, sizeof(struct roffcol)); 199 200 hp = sp->head; 201 202 for ( ; sp; sp = sp->next) { 203 if (TBL_SPAN_DATA != sp->pos) 204 continue; 205 /* 206 * Account for the data cells in the layout, matching it 207 * to data cells in the data section. 208 */ 209 for (dp = sp->first; dp; dp = dp->next) { 210 assert(dp->layout); 211 col = &tbl->cols[dp->layout->head->ident]; 212 tblcalc_data(tbl, col, sp->tbl, dp); 213 } 214 } 215 216 /* 217 * Calculate width of the spanners. These get one space for a 218 * vertical line, two for a double-vertical line. 219 */ 220 221 for ( ; hp; hp = hp->next) { 222 col = &tbl->cols[hp->ident]; 223 switch (hp->pos) { 224 case (TBL_HEAD_VERT): 225 col->width = (*tbl->len)(1, tbl->arg); 226 break; 227 case (TBL_HEAD_DVERT): 228 col->width = (*tbl->len)(2, tbl->arg); 229 break; 230 default: 231 break; 232 } 233 } 234 } 235 236 static void 237 tblcalc_data(struct rofftbl *tbl, struct roffcol *col, 238 const struct tbl *tp, const struct tbl_dat *dp) 239 { 240 size_t sz; 241 242 /* Branch down into data sub-types. */ 243 244 switch (dp->layout->pos) { 245 case (TBL_CELL_HORIZ): 246 /* FALLTHROUGH */ 247 case (TBL_CELL_DHORIZ): 248 sz = (*tbl->len)(1, tbl->arg); 249 if (col->width < sz) 250 col->width = sz; 251 break; 252 case (TBL_CELL_LONG): 253 /* FALLTHROUGH */ 254 case (TBL_CELL_CENTRE): 255 /* FALLTHROUGH */ 256 case (TBL_CELL_LEFT): 257 /* FALLTHROUGH */ 258 case (TBL_CELL_RIGHT): 259 tblcalc_literal(tbl, col, dp); 260 break; 261 case (TBL_CELL_NUMBER): 262 tblcalc_number(tbl, col, tp, dp); 263 break; 264 case (TBL_CELL_DOWN): 265 break; 266 default: 267 abort(); 268 /* NOTREACHED */ 269 } 270 } 271 272 static void 273 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, 274 const struct tbl_dat *dp) 275 { 276 size_t sz, bufsz, spsz; 277 const char *str; 278 279 /* 280 * Calculate our width and use the spacing, with a minimum 281 * spacing dictated by position (centre, e.g,. gets a space on 282 * either side, while right/left get a single adjacent space). 283 */ 284 285 bufsz = spsz = 0; 286 str = dp->string ? dp->string : ""; 287 sz = (*tbl->slen)(str, tbl->arg); 288 289 /* FIXME: TBL_DATA_HORIZ et al.? */ 290 291 assert(dp->layout); 292 switch (dp->layout->pos) { 293 case (TBL_CELL_LONG): 294 /* FALLTHROUGH */ 295 case (TBL_CELL_CENTRE): 296 bufsz = (*tbl->len)(1, tbl->arg); 297 break; 298 default: 299 bufsz = (*tbl->len)(1, tbl->arg); 300 break; 301 } 302 303 if (dp->layout->spacing) { 304 spsz = (*tbl->len)(dp->layout->spacing, tbl->arg); 305 bufsz = bufsz > spsz ? bufsz : spsz; 306 } 307 308 sz += bufsz; 309 if (col->width < sz) 310 col->width = sz; 311 } 312 313 static void 314 tblcalc_number(struct rofftbl *tbl, struct roffcol *col, 315 const struct tbl *tp, const struct tbl_dat *dp) 316 { 317 int i; 318 size_t sz, psz, ssz, d; 319 const char *str; 320 char *cp; 321 char buf[2]; 322 323 /* 324 * First calculate number width and decimal place (last + 1 for 325 * no-decimal numbers). If the stored decimal is subsequent 326 * ours, make our size longer by that difference 327 * (right-"shifting"); similarly, if ours is subsequent the 328 * stored, then extend the stored size by the difference. 329 * Finally, re-assign the stored values. 330 */ 331 332 str = dp->string ? dp->string : ""; 333 sz = (*tbl->slen)(str, tbl->arg); 334 335 /* FIXME: TBL_DATA_HORIZ et al.? */ 336 337 buf[0] = tp->decimal; 338 buf[1] = '\0'; 339 340 psz = (*tbl->slen)(buf, tbl->arg); 341 342 if (NULL != (cp = strrchr(str, tp->decimal))) { 343 buf[1] = '\0'; 344 for (ssz = 0, i = 0; cp != &str[i]; i++) { 345 buf[0] = str[i]; 346 ssz += (*tbl->slen)(buf, tbl->arg); 347 } 348 d = ssz + psz; 349 } else 350 d = sz + psz; 351 352 /* Padding. */ 353 354 sz += (*tbl->len)(2, tbl->arg); 355 d += (*tbl->len)(1, tbl->arg); 356 357 /* Adjust the settings for this column. */ 358 359 if (col->decimal > d) { 360 sz += col->decimal - d; 361 d = col->decimal; 362 } else 363 col->width += d - col->decimal; 364 365 if (sz > col->width) 366 col->width = sz; 367 if (d > col->decimal) 368 col->decimal = d; 369 370 /* Adjust for stipulated width. */ 371 372 if (col->width < dp->layout->spacing) 373 col->width = dp->layout->spacing; 374 } 375 376 377