1 /* $Id: out.c,v 1.30 2011/01/05 15:37:23 kristaps Exp $ */
3 * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/types.h>
33 static void tblcalc_data(struct rofftbl *, struct roffcol *,
34 const struct tbl *, const struct tbl_dat *);
35 static void tblcalc_literal(struct rofftbl *, struct roffcol *,
36 const struct tbl_dat *);
37 static void tblcalc_number(struct rofftbl *, struct roffcol *,
38 const struct tbl *, const struct tbl_dat *);
41 * Convert a `scaling unit' to a consistent form, or fail. Scaling
42 * units are documented in groff.7, mdoc.7, man.7.
45 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
47 char buf[BUFSIZ], hasd;
71 if ( ! isdigit((u_char)*src)) {
82 if (BUFSIZ == i || (*src && *(src + 1)))
110 if (SCALE_MAX == def)
127 /* FIXME: do this in the caller. */
128 if ((dst->scale = atof(buf)) < 0)
136 * Correctly writes the time in nroff form, which differs from standard
137 * form in that a space isn't printed in lieu of the extra %e field for
138 * single-digit dates.
141 time2a(time_t t, char *dst, size_t sz)
149 localtime_r(&t, &tm);
156 if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
162 if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
165 nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
173 (void)strftime(p, sz, "%Y", &tm);
178 a2roffdeco(enum roffdeco *d, const char **word, size_t *sz)
190 switch ((c = wp[i++])) {
198 *d = 'F' == c ? DECO_FFONT : DECO_FONT;
260 if ('+' == wp[i] || '-' == wp[i]) {
284 if ('+' == wp[i] || '-' == wp[i]) {
290 /* Handle embedded numerical subexp or escape. */
293 while (wp[i] && ')' != wp[i])
294 if ('\\' == wp[i++]) {
295 /* Handle embedded escape. */
297 i += a2roffdeco(&dd, word, sz);
305 } else if ('\\' == wp[i]) {
307 i += a2roffdeco(&dd, word, sz);
322 return(i + a2roffdeco(&dd, word, sz));
329 if ('\'' == wp[i++]) {
346 while (wp[i] && wp[i] != term)
354 *sz = (size_t)(i - j);
362 for (j = 0; wp[i] && j < lim; j++)
371 * Calculate the abstract widths and decimal positions of columns in a
372 * table. This routine allocates the columns structures then runs over
373 * all rows and cells in the table. The function pointers in "tbl" are
374 * used for the actual width calculations.
377 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
379 const struct tbl_dat *dp;
380 const struct tbl_head *hp;
384 * Allocate the master column specifiers. These will hold the
385 * widths and decimal positions for all cells in the column. It
386 * must be freed and nullified by the caller.
389 assert(NULL == tbl->cols);
390 tbl->cols = calloc(sp->tbl->cols, sizeof(struct roffcol));
394 for ( ; sp; sp = sp->next) {
395 if (TBL_SPAN_DATA != sp->pos)
398 * Account for the data cells in the layout, matching it
399 * to data cells in the data section.
401 for (dp = sp->first; dp; dp = dp->next) {
402 if (NULL == dp->layout)
404 col = &tbl->cols[dp->layout->head->ident];
405 tblcalc_data(tbl, col, sp->tbl, dp);
410 * Calculate width of the spanners. These get one space for a
411 * vertical line, two for a double-vertical line.
414 for ( ; hp; hp = hp->next) {
415 col = &tbl->cols[hp->ident];
417 case (TBL_HEAD_VERT):
418 col->width = (*tbl->len)(1, tbl->arg);
420 case (TBL_HEAD_DVERT):
421 col->width = (*tbl->len)(2, tbl->arg);
430 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
431 const struct tbl *tp, const struct tbl_dat *dp)
435 /* Branch down into data sub-types. */
437 switch (dp->layout->pos) {
438 case (TBL_CELL_HORIZ):
440 case (TBL_CELL_DHORIZ):
441 sz = (*tbl->len)(1, tbl->arg);
445 case (TBL_CELL_LONG):
447 case (TBL_CELL_CENTRE):
449 case (TBL_CELL_LEFT):
451 case (TBL_CELL_RIGHT):
452 tblcalc_literal(tbl, col, dp);
454 case (TBL_CELL_NUMBER):
455 tblcalc_number(tbl, col, tp, dp);
464 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
465 const struct tbl_dat *dp)
467 size_t sz, bufsz, spsz;
470 * Calculate our width and use the spacing, with a minimum
471 * spacing dictated by position (centre, e.g,. gets a space on
472 * either side, while right/left get a single adjacent space).
475 sz = bufsz = spsz = 0;
477 sz = (*tbl->slen)(dp->string, tbl->arg);
480 switch (dp->layout->pos) {
481 case (TBL_CELL_LONG):
483 case (TBL_CELL_CENTRE):
484 bufsz = (*tbl->len)(2, tbl->arg);
487 bufsz = (*tbl->len)(1, tbl->arg);
491 if (dp->layout->spacing) {
492 spsz = (*tbl->len)(dp->layout->spacing, tbl->arg);
493 bufsz = bufsz > spsz ? bufsz : spsz;
502 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
503 const struct tbl *tp, const struct tbl_dat *dp)
506 size_t sz, psz, ssz, d;
511 /* TODO: use spacing modifier. */
514 * First calculate number width and decimal place (last + 1 for
515 * no-decimal numbers). If the stored decimal is subsequent
516 * ours, make our size longer by that difference
517 * (right-"shifting"); similarly, if ours is subsequent the
518 * stored, then extend the stored size by the difference.
519 * Finally, re-assign the stored values.
526 sz = (*tbl->slen)(str, tbl->arg);
528 buf[0] = tp->decimal;
531 psz = (*tbl->slen)(buf, tbl->arg);
533 if (NULL != (cp = strchr(str, tp->decimal))) {
535 for (ssz = 0, i = 0; cp != &str[i]; i++) {
537 ssz += (*tbl->slen)(buf, tbl->arg);
545 sz += (*tbl->len)(2, tbl->arg);
546 d += (*tbl->len)(1, tbl->arg);
548 /* Adjust the settings for this column. */
550 if (col->decimal > d) {
551 sz += col->decimal - d;
554 col->width += d - col->decimal;
558 if (d > col->decimal)