1 /* $Id: out.c,v 1.39 2011/03/17 08:49:34 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 int 179 a2roffdeco(enum roffdeco *d, const char **word, size_t *sz) 180 { 181 int i, j, lim; 182 char term, c; 183 const char *wp; 184 enum roffdeco dd; 185 186 *d = DECO_NONE; 187 lim = i = 0; 188 term = '\0'; 189 wp = *word; 190 191 switch ((c = wp[i++])) { 192 case ('('): 193 *d = DECO_SPECIAL; 194 lim = 2; 195 break; 196 case ('F'): 197 /* FALLTHROUGH */ 198 case ('f'): 199 *d = 'F' == c ? DECO_FFONT : DECO_FONT; 200 201 switch (wp[i++]) { 202 case ('('): 203 lim = 2; 204 break; 205 case ('['): 206 term = ']'; 207 break; 208 case ('3'): 209 /* FALLTHROUGH */ 210 case ('B'): 211 *d = DECO_BOLD; 212 return(i); 213 case ('2'): 214 /* FALLTHROUGH */ 215 case ('I'): 216 *d = DECO_ITALIC; 217 return(i); 218 case ('P'): 219 *d = DECO_PREVIOUS; 220 return(i); 221 case ('1'): 222 /* FALLTHROUGH */ 223 case ('R'): 224 *d = DECO_ROMAN; 225 return(i); 226 default: 227 i--; 228 lim = 1; 229 break; 230 } 231 break; 232 case ('k'): 233 /* FALLTHROUGH */ 234 case ('M'): 235 /* FALLTHROUGH */ 236 case ('m'): 237 /* FALLTHROUGH */ 238 case ('*'): 239 if ('*' == c) 240 *d = DECO_RESERVED; 241 242 switch (wp[i++]) { 243 case ('('): 244 lim = 2; 245 break; 246 case ('['): 247 term = ']'; 248 break; 249 default: 250 i--; 251 lim = 1; 252 break; 253 } 254 break; 255 256 case ('N'): 257 258 /* 259 * Sequence of characters: backslash, 'N' (i = 0), 260 * starting delimiter (i = 1), character number (i = 2). 261 */ 262 263 *word = wp + 2; 264 *sz = 0; 265 266 /* 267 * Cannot use a digit as a starting delimiter; 268 * but skip the digit anyway. 269 */ 270 271 if (isdigit((int)wp[1])) 272 return(2); 273 274 /* 275 * Any non-digit terminates the character number. 276 * That is, the terminating delimiter need not 277 * match the starting delimiter. 278 */ 279 280 for (i = 2; isdigit((int)wp[i]); i++) 281 (*sz)++; 282 283 /* 284 * This is only a numbered character 285 * if the character number has at least one digit. 286 */ 287 288 if (*sz) 289 *d = DECO_NUMBERED; 290 291 /* 292 * Skip the terminating delimiter, even if it does not 293 * match, and even if there is no character number. 294 */ 295 296 return(++i); 297 298 case ('h'): 299 /* FALLTHROUGH */ 300 case ('v'): 301 /* FALLTHROUGH */ 302 case ('s'): 303 j = 0; 304 if ('+' == wp[i] || '-' == wp[i]) { 305 i++; 306 j = 1; 307 } 308 309 switch (wp[i++]) { 310 case ('('): 311 lim = 2; 312 break; 313 case ('['): 314 term = ']'; 315 break; 316 case ('\''): 317 term = '\''; 318 break; 319 case ('0'): 320 j = 1; 321 /* FALLTHROUGH */ 322 default: 323 i--; 324 lim = 1; 325 break; 326 } 327 328 if ('+' == wp[i] || '-' == wp[i]) { 329 if (j) 330 return(i); 331 i++; 332 } 333 334 /* Handle embedded numerical subexp or escape. */ 335 336 if ('(' == wp[i]) { 337 while (wp[i] && ')' != wp[i]) 338 if ('\\' == wp[i++]) { 339 /* Handle embedded escape. */ 340 *word = &wp[i]; 341 i += a2roffdeco(&dd, word, sz); 342 } 343 344 if (')' == wp[i++]) 345 break; 346 347 *d = DECO_NONE; 348 return(i - 1); 349 } else if ('\\' == wp[i]) { 350 *word = &wp[++i]; 351 i += a2roffdeco(&dd, word, sz); 352 } 353 354 break; 355 case ('['): 356 *d = DECO_SPECIAL; 357 term = ']'; 358 break; 359 case ('c'): 360 *d = DECO_NOSPACE; 361 return(i); 362 case ('z'): 363 *d = DECO_NONE; 364 if ('\\' == wp[i]) { 365 *word = &wp[++i]; 366 return(i + a2roffdeco(&dd, word, sz)); 367 } else 368 lim = 1; 369 break; 370 case ('o'): 371 /* FALLTHROUGH */ 372 case ('w'): 373 if ('\'' == wp[i++]) { 374 term = '\''; 375 break; 376 } 377 /* FALLTHROUGH */ 378 default: 379 *d = DECO_SSPECIAL; 380 i--; 381 lim = 1; 382 break; 383 } 384 385 assert(term || lim); 386 *word = &wp[i]; 387 388 if (term) { 389 j = i; 390 while (wp[i] && wp[i] != term) 391 i++; 392 if ('\0' == wp[i]) { 393 *d = DECO_NONE; 394 return(i); 395 } 396 397 assert(i >= j); 398 *sz = (size_t)(i - j); 399 400 return(i + 1); 401 } 402 403 assert(lim > 0); 404 *sz = (size_t)lim; 405 406 for (j = 0; wp[i] && j < lim; j++) 407 i++; 408 if (j < lim) 409 *d = DECO_NONE; 410 411 return(i); 412 } 413 414 /* 415 * Calculate the abstract widths and decimal positions of columns in a 416 * table. This routine allocates the columns structures then runs over 417 * all rows and cells in the table. The function pointers in "tbl" are 418 * used for the actual width calculations. 419 */ 420 void 421 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp) 422 { 423 const struct tbl_dat *dp; 424 const struct tbl_head *hp; 425 struct roffcol *col; 426 427 /* 428 * Allocate the master column specifiers. These will hold the 429 * widths and decimal positions for all cells in the column. It 430 * must be freed and nullified by the caller. 431 */ 432 433 assert(NULL == tbl->cols); 434 tbl->cols = mandoc_calloc 435 ((size_t)sp->tbl->cols, sizeof(struct roffcol)); 436 437 hp = sp->head; 438 439 for ( ; sp; sp = sp->next) { 440 if (TBL_SPAN_DATA != sp->pos) 441 continue; 442 /* 443 * Account for the data cells in the layout, matching it 444 * to data cells in the data section. 445 */ 446 for (dp = sp->first; dp; dp = dp->next) { 447 assert(dp->layout); 448 col = &tbl->cols[dp->layout->head->ident]; 449 tblcalc_data(tbl, col, sp->tbl, dp); 450 } 451 } 452 453 /* 454 * Calculate width of the spanners. These get one space for a 455 * vertical line, two for a double-vertical line. 456 */ 457 458 for ( ; hp; hp = hp->next) { 459 col = &tbl->cols[hp->ident]; 460 switch (hp->pos) { 461 case (TBL_HEAD_VERT): 462 col->width = (*tbl->len)(1, tbl->arg); 463 break; 464 case (TBL_HEAD_DVERT): 465 col->width = (*tbl->len)(2, tbl->arg); 466 break; 467 default: 468 break; 469 } 470 } 471 } 472 473 static void 474 tblcalc_data(struct rofftbl *tbl, struct roffcol *col, 475 const struct tbl *tp, const struct tbl_dat *dp) 476 { 477 size_t sz; 478 479 /* Branch down into data sub-types. */ 480 481 switch (dp->layout->pos) { 482 case (TBL_CELL_HORIZ): 483 /* FALLTHROUGH */ 484 case (TBL_CELL_DHORIZ): 485 sz = (*tbl->len)(1, tbl->arg); 486 if (col->width < sz) 487 col->width = sz; 488 break; 489 case (TBL_CELL_LONG): 490 /* FALLTHROUGH */ 491 case (TBL_CELL_CENTRE): 492 /* FALLTHROUGH */ 493 case (TBL_CELL_LEFT): 494 /* FALLTHROUGH */ 495 case (TBL_CELL_RIGHT): 496 tblcalc_literal(tbl, col, dp); 497 break; 498 case (TBL_CELL_NUMBER): 499 tblcalc_number(tbl, col, tp, dp); 500 break; 501 case (TBL_CELL_DOWN): 502 break; 503 default: 504 abort(); 505 /* NOTREACHED */ 506 } 507 } 508 509 static void 510 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, 511 const struct tbl_dat *dp) 512 { 513 size_t sz, bufsz, spsz; 514 const char *str; 515 516 /* 517 * Calculate our width and use the spacing, with a minimum 518 * spacing dictated by position (centre, e.g,. gets a space on 519 * either side, while right/left get a single adjacent space). 520 */ 521 522 bufsz = spsz = 0; 523 str = dp->string ? dp->string : ""; 524 sz = (*tbl->slen)(str, tbl->arg); 525 526 /* FIXME: TBL_DATA_HORIZ et al.? */ 527 528 assert(dp->layout); 529 switch (dp->layout->pos) { 530 case (TBL_CELL_LONG): 531 /* FALLTHROUGH */ 532 case (TBL_CELL_CENTRE): 533 bufsz = (*tbl->len)(1, tbl->arg); 534 break; 535 default: 536 bufsz = (*tbl->len)(1, tbl->arg); 537 break; 538 } 539 540 if (dp->layout->spacing) { 541 spsz = (*tbl->len)(dp->layout->spacing, tbl->arg); 542 bufsz = bufsz > spsz ? bufsz : spsz; 543 } 544 545 sz += bufsz; 546 if (col->width < sz) 547 col->width = sz; 548 } 549 550 static void 551 tblcalc_number(struct rofftbl *tbl, struct roffcol *col, 552 const struct tbl *tp, const struct tbl_dat *dp) 553 { 554 int i; 555 size_t sz, psz, ssz, d; 556 const char *str; 557 char *cp; 558 char buf[2]; 559 560 /* 561 * First calculate number width and decimal place (last + 1 for 562 * no-decimal numbers). If the stored decimal is subsequent 563 * ours, make our size longer by that difference 564 * (right-"shifting"); similarly, if ours is subsequent the 565 * stored, then extend the stored size by the difference. 566 * Finally, re-assign the stored values. 567 */ 568 569 str = dp->string ? dp->string : ""; 570 sz = (*tbl->slen)(str, tbl->arg); 571 572 /* FIXME: TBL_DATA_HORIZ et al.? */ 573 574 buf[0] = tp->decimal; 575 buf[1] = '\0'; 576 577 psz = (*tbl->slen)(buf, tbl->arg); 578 579 if (NULL != (cp = strrchr(str, tp->decimal))) { 580 buf[1] = '\0'; 581 for (ssz = 0, i = 0; cp != &str[i]; i++) { 582 buf[0] = str[i]; 583 ssz += (*tbl->slen)(buf, tbl->arg); 584 } 585 d = ssz + psz; 586 } else 587 d = sz + psz; 588 589 /* Padding. */ 590 591 sz += (*tbl->len)(2, tbl->arg); 592 d += (*tbl->len)(1, tbl->arg); 593 594 /* Adjust the settings for this column. */ 595 596 if (col->decimal > d) { 597 sz += col->decimal - d; 598 d = col->decimal; 599 } else 600 col->width += d - col->decimal; 601 602 if (sz > col->width) 603 col->width = sz; 604 if (d > col->decimal) 605 col->decimal = d; 606 607 /* Adjust for stipulated width. */ 608 609 if (col->width < dp->layout->spacing) 610 col->width = dp->layout->spacing; 611 } 612 613 614