1 /* $Id: out.c,v 1.39 2011/03/17 08:49:34 kristaps Exp $ */
3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
22 #include <sys/types.h>
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 *);
42 * Convert a `scaling unit' to a consistent form, or fail. Scaling
43 * units are documented in groff.7, mdoc.7, man.7.
46 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
48 char buf[BUFSIZ], hasd;
72 if ( ! isdigit((u_char)*src)) {
83 if (BUFSIZ == i || (*src && *(src + 1)))
111 if (SCALE_MAX == def)
128 /* FIXME: do this in the caller. */
129 if ((dst->scale = atof(buf)) < 0)
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.
142 time2a(time_t t, char *dst, size_t sz)
150 localtime_r(&t, &tm);
157 if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
163 if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
166 nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
174 (void)strftime(p, sz, "%Y", &tm);
179 a2roffdeco(enum roffdeco *d, const char **word, size_t *sz)
191 switch ((c = wp[i++])) {
199 *d = 'F' == c ? DECO_FFONT : DECO_FONT;
259 * Sequence of characters: backslash, 'N' (i = 0),
260 * starting delimiter (i = 1), character number (i = 2).
267 * Cannot use a digit as a starting delimiter;
268 * but skip the digit anyway.
271 if (isdigit((int)wp[1]))
275 * Any non-digit terminates the character number.
276 * That is, the terminating delimiter need not
277 * match the starting delimiter.
280 for (i = 2; isdigit((int)wp[i]); i++)
284 * This is only a numbered character
285 * if the character number has at least one digit.
292 * Skip the terminating delimiter, even if it does not
293 * match, and even if there is no character number.
304 if ('+' == wp[i] || '-' == wp[i]) {
328 if ('+' == wp[i] || '-' == wp[i]) {
334 /* Handle embedded numerical subexp or escape. */
337 while (wp[i] && ')' != wp[i])
338 if ('\\' == wp[i++]) {
339 /* Handle embedded escape. */
341 i += a2roffdeco(&dd, word, sz);
349 } else if ('\\' == wp[i]) {
351 i += a2roffdeco(&dd, word, sz);
366 return(i + a2roffdeco(&dd, word, sz));
373 if ('\'' == wp[i++]) {
390 while (wp[i] && wp[i] != term)
398 *sz = (size_t)(i - j);
406 for (j = 0; wp[i] && j < lim; j++)
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.
421 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
423 const struct tbl_dat *dp;
424 const struct tbl_head *hp;
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.
433 assert(NULL == tbl->cols);
434 tbl->cols = mandoc_calloc
435 ((size_t)sp->tbl->cols, sizeof(struct roffcol));
439 for ( ; sp; sp = sp->next) {
440 if (TBL_SPAN_DATA != sp->pos)
443 * Account for the data cells in the layout, matching it
444 * to data cells in the data section.
446 for (dp = sp->first; dp; dp = dp->next) {
448 col = &tbl->cols[dp->layout->head->ident];
449 tblcalc_data(tbl, col, sp->tbl, dp);
454 * Calculate width of the spanners. These get one space for a
455 * vertical line, two for a double-vertical line.
458 for ( ; hp; hp = hp->next) {
459 col = &tbl->cols[hp->ident];
461 case (TBL_HEAD_VERT):
462 col->width = (*tbl->len)(1, tbl->arg);
464 case (TBL_HEAD_DVERT):
465 col->width = (*tbl->len)(2, tbl->arg);
474 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
475 const struct tbl *tp, const struct tbl_dat *dp)
479 /* Branch down into data sub-types. */
481 switch (dp->layout->pos) {
482 case (TBL_CELL_HORIZ):
484 case (TBL_CELL_DHORIZ):
485 sz = (*tbl->len)(1, tbl->arg);
489 case (TBL_CELL_LONG):
491 case (TBL_CELL_CENTRE):
493 case (TBL_CELL_LEFT):
495 case (TBL_CELL_RIGHT):
496 tblcalc_literal(tbl, col, dp);
498 case (TBL_CELL_NUMBER):
499 tblcalc_number(tbl, col, tp, dp);
501 case (TBL_CELL_DOWN):
510 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
511 const struct tbl_dat *dp)
513 size_t sz, bufsz, spsz;
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).
523 str = dp->string ? dp->string : "";
524 sz = (*tbl->slen)(str, tbl->arg);
526 /* FIXME: TBL_DATA_HORIZ et al.? */
529 switch (dp->layout->pos) {
530 case (TBL_CELL_LONG):
532 case (TBL_CELL_CENTRE):
533 bufsz = (*tbl->len)(1, tbl->arg);
536 bufsz = (*tbl->len)(1, tbl->arg);
540 if (dp->layout->spacing) {
541 spsz = (*tbl->len)(dp->layout->spacing, tbl->arg);
542 bufsz = bufsz > spsz ? bufsz : spsz;
551 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
552 const struct tbl *tp, const struct tbl_dat *dp)
555 size_t sz, psz, ssz, d;
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.
569 str = dp->string ? dp->string : "";
570 sz = (*tbl->slen)(str, tbl->arg);
572 /* FIXME: TBL_DATA_HORIZ et al.? */
574 buf[0] = tp->decimal;
577 psz = (*tbl->slen)(buf, tbl->arg);
579 if (NULL != (cp = strrchr(str, tp->decimal))) {
581 for (ssz = 0, i = 0; cp != &str[i]; i++) {
583 ssz += (*tbl->slen)(buf, tbl->arg);
591 sz += (*tbl->len)(2, tbl->arg);
592 d += (*tbl->len)(1, tbl->arg);
594 /* Adjust the settings for this column. */
596 if (col->decimal > d) {
597 sz += col->decimal - d;
600 col->width += d - col->decimal;
604 if (d > col->decimal)
607 /* Adjust for stipulated width. */
609 if (col->width < dp->layout->spacing)
610 col->width = dp->layout->spacing;