1*ffb8ebfaSGarrett D'Amore /* $Id: out.c,v 1.46 2013/10/05 20:30:05 schwarze Exp $ */
232a712daSGarrett D'Amore /*
332a712daSGarrett D'Amore * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
432a712daSGarrett D'Amore * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
532a712daSGarrett D'Amore *
632a712daSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any
732a712daSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above
832a712daSGarrett D'Amore * copyright notice and this permission notice appear in all copies.
932a712daSGarrett D'Amore *
1032a712daSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1132a712daSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1232a712daSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1332a712daSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1432a712daSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1532a712daSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1632a712daSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1732a712daSGarrett D'Amore */
1832a712daSGarrett D'Amore #ifdef HAVE_CONFIG_H
1932a712daSGarrett D'Amore #include "config.h"
2032a712daSGarrett D'Amore #endif
2132a712daSGarrett D'Amore
2232a712daSGarrett D'Amore #include <sys/types.h>
2332a712daSGarrett D'Amore
2432a712daSGarrett D'Amore #include <assert.h>
2532a712daSGarrett D'Amore #include <ctype.h>
2632a712daSGarrett D'Amore #include <stdio.h>
2732a712daSGarrett D'Amore #include <stdlib.h>
2832a712daSGarrett D'Amore #include <string.h>
2932a712daSGarrett D'Amore #include <time.h>
3032a712daSGarrett D'Amore
3132a712daSGarrett D'Amore #include "mandoc.h"
3232a712daSGarrett D'Amore #include "out.h"
3332a712daSGarrett D'Amore
3432a712daSGarrett D'Amore static void tblcalc_data(struct rofftbl *, struct roffcol *,
35*ffb8ebfaSGarrett D'Amore const struct tbl_opts *, const struct tbl_dat *);
3632a712daSGarrett D'Amore static void tblcalc_literal(struct rofftbl *, struct roffcol *,
3732a712daSGarrett D'Amore const struct tbl_dat *);
3832a712daSGarrett D'Amore static void tblcalc_number(struct rofftbl *, struct roffcol *,
39*ffb8ebfaSGarrett D'Amore const struct tbl_opts *, const struct tbl_dat *);
4032a712daSGarrett D'Amore
4132a712daSGarrett D'Amore /*
4232a712daSGarrett D'Amore * Convert a `scaling unit' to a consistent form, or fail. Scaling
4332a712daSGarrett D'Amore * units are documented in groff.7, mdoc.7, man.7.
4432a712daSGarrett D'Amore */
4532a712daSGarrett D'Amore int
a2roffsu(const char * src,struct roffsu * dst,enum roffscale def)4632a712daSGarrett D'Amore a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
4732a712daSGarrett D'Amore {
4832a712daSGarrett D'Amore char buf[BUFSIZ], hasd;
4932a712daSGarrett D'Amore int i;
5032a712daSGarrett D'Amore enum roffscale unit;
5132a712daSGarrett D'Amore
5232a712daSGarrett D'Amore if ('\0' == *src)
5332a712daSGarrett D'Amore return(0);
5432a712daSGarrett D'Amore
5532a712daSGarrett D'Amore i = hasd = 0;
5632a712daSGarrett D'Amore
5732a712daSGarrett D'Amore switch (*src) {
5832a712daSGarrett D'Amore case ('+'):
5932a712daSGarrett D'Amore src++;
6032a712daSGarrett D'Amore break;
6132a712daSGarrett D'Amore case ('-'):
6232a712daSGarrett D'Amore buf[i++] = *src++;
6332a712daSGarrett D'Amore break;
6432a712daSGarrett D'Amore default:
6532a712daSGarrett D'Amore break;
6632a712daSGarrett D'Amore }
6732a712daSGarrett D'Amore
6832a712daSGarrett D'Amore if ('\0' == *src)
6932a712daSGarrett D'Amore return(0);
7032a712daSGarrett D'Amore
7132a712daSGarrett D'Amore while (i < BUFSIZ) {
7232a712daSGarrett D'Amore if ( ! isdigit((unsigned char)*src)) {
7332a712daSGarrett D'Amore if ('.' != *src)
7432a712daSGarrett D'Amore break;
7532a712daSGarrett D'Amore else if (hasd)
7632a712daSGarrett D'Amore break;
7732a712daSGarrett D'Amore else
7832a712daSGarrett D'Amore hasd = 1;
7932a712daSGarrett D'Amore }
8032a712daSGarrett D'Amore buf[i++] = *src++;
8132a712daSGarrett D'Amore }
8232a712daSGarrett D'Amore
8332a712daSGarrett D'Amore if (BUFSIZ == i || (*src && *(src + 1)))
8432a712daSGarrett D'Amore return(0);
8532a712daSGarrett D'Amore
8632a712daSGarrett D'Amore buf[i] = '\0';
8732a712daSGarrett D'Amore
8832a712daSGarrett D'Amore switch (*src) {
8932a712daSGarrett D'Amore case ('c'):
9032a712daSGarrett D'Amore unit = SCALE_CM;
9132a712daSGarrett D'Amore break;
9232a712daSGarrett D'Amore case ('i'):
9332a712daSGarrett D'Amore unit = SCALE_IN;
9432a712daSGarrett D'Amore break;
9532a712daSGarrett D'Amore case ('P'):
9632a712daSGarrett D'Amore unit = SCALE_PC;
9732a712daSGarrett D'Amore break;
9832a712daSGarrett D'Amore case ('p'):
9932a712daSGarrett D'Amore unit = SCALE_PT;
10032a712daSGarrett D'Amore break;
10132a712daSGarrett D'Amore case ('f'):
10232a712daSGarrett D'Amore unit = SCALE_FS;
10332a712daSGarrett D'Amore break;
10432a712daSGarrett D'Amore case ('v'):
10532a712daSGarrett D'Amore unit = SCALE_VS;
10632a712daSGarrett D'Amore break;
10732a712daSGarrett D'Amore case ('m'):
10832a712daSGarrett D'Amore unit = SCALE_EM;
10932a712daSGarrett D'Amore break;
11032a712daSGarrett D'Amore case ('\0'):
11132a712daSGarrett D'Amore if (SCALE_MAX == def)
11232a712daSGarrett D'Amore return(0);
11332a712daSGarrett D'Amore unit = SCALE_BU;
11432a712daSGarrett D'Amore break;
11532a712daSGarrett D'Amore case ('u'):
11632a712daSGarrett D'Amore unit = SCALE_BU;
11732a712daSGarrett D'Amore break;
11832a712daSGarrett D'Amore case ('M'):
11932a712daSGarrett D'Amore unit = SCALE_MM;
12032a712daSGarrett D'Amore break;
12132a712daSGarrett D'Amore case ('n'):
12232a712daSGarrett D'Amore unit = SCALE_EN;
12332a712daSGarrett D'Amore break;
12432a712daSGarrett D'Amore default:
12532a712daSGarrett D'Amore return(0);
12632a712daSGarrett D'Amore }
12732a712daSGarrett D'Amore
12832a712daSGarrett D'Amore /* FIXME: do this in the caller. */
12932a712daSGarrett D'Amore if ((dst->scale = atof(buf)) < 0)
13032a712daSGarrett D'Amore dst->scale = 0;
13132a712daSGarrett D'Amore dst->unit = unit;
13232a712daSGarrett D'Amore return(1);
13332a712daSGarrett D'Amore }
13432a712daSGarrett D'Amore
13532a712daSGarrett D'Amore /*
13632a712daSGarrett D'Amore * Calculate the abstract widths and decimal positions of columns in a
13732a712daSGarrett D'Amore * table. This routine allocates the columns structures then runs over
13832a712daSGarrett D'Amore * all rows and cells in the table. The function pointers in "tbl" are
13932a712daSGarrett D'Amore * used for the actual width calculations.
14032a712daSGarrett D'Amore */
14132a712daSGarrett D'Amore void
tblcalc(struct rofftbl * tbl,const struct tbl_span * sp)14232a712daSGarrett D'Amore tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
14332a712daSGarrett D'Amore {
14432a712daSGarrett D'Amore const struct tbl_dat *dp;
14532a712daSGarrett D'Amore struct roffcol *col;
14632a712daSGarrett D'Amore int spans;
14732a712daSGarrett D'Amore
14832a712daSGarrett D'Amore /*
14932a712daSGarrett D'Amore * Allocate the master column specifiers. These will hold the
15032a712daSGarrett D'Amore * widths and decimal positions for all cells in the column. It
15132a712daSGarrett D'Amore * must be freed and nullified by the caller.
15232a712daSGarrett D'Amore */
15332a712daSGarrett D'Amore
15432a712daSGarrett D'Amore assert(NULL == tbl->cols);
15532a712daSGarrett D'Amore tbl->cols = mandoc_calloc
156*ffb8ebfaSGarrett D'Amore ((size_t)sp->opts->cols, sizeof(struct roffcol));
15732a712daSGarrett D'Amore
15832a712daSGarrett D'Amore for ( ; sp; sp = sp->next) {
15932a712daSGarrett D'Amore if (TBL_SPAN_DATA != sp->pos)
16032a712daSGarrett D'Amore continue;
16132a712daSGarrett D'Amore spans = 1;
16232a712daSGarrett D'Amore /*
16332a712daSGarrett D'Amore * Account for the data cells in the layout, matching it
16432a712daSGarrett D'Amore * to data cells in the data section.
16532a712daSGarrett D'Amore */
16632a712daSGarrett D'Amore for (dp = sp->first; dp; dp = dp->next) {
16732a712daSGarrett D'Amore /* Do not used spanned cells in the calculation. */
16832a712daSGarrett D'Amore if (0 < --spans)
16932a712daSGarrett D'Amore continue;
17032a712daSGarrett D'Amore spans = dp->spans;
17132a712daSGarrett D'Amore if (1 < spans)
17232a712daSGarrett D'Amore continue;
17332a712daSGarrett D'Amore assert(dp->layout);
17432a712daSGarrett D'Amore col = &tbl->cols[dp->layout->head->ident];
175*ffb8ebfaSGarrett D'Amore tblcalc_data(tbl, col, sp->opts, dp);
17632a712daSGarrett D'Amore }
17732a712daSGarrett D'Amore }
17832a712daSGarrett D'Amore }
17932a712daSGarrett D'Amore
18032a712daSGarrett D'Amore static void
tblcalc_data(struct rofftbl * tbl,struct roffcol * col,const struct tbl_opts * opts,const struct tbl_dat * dp)18132a712daSGarrett D'Amore tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
182*ffb8ebfaSGarrett D'Amore const struct tbl_opts *opts, const struct tbl_dat *dp)
18332a712daSGarrett D'Amore {
18432a712daSGarrett D'Amore size_t sz;
18532a712daSGarrett D'Amore
18632a712daSGarrett D'Amore /* Branch down into data sub-types. */
18732a712daSGarrett D'Amore
18832a712daSGarrett D'Amore switch (dp->layout->pos) {
18932a712daSGarrett D'Amore case (TBL_CELL_HORIZ):
19032a712daSGarrett D'Amore /* FALLTHROUGH */
19132a712daSGarrett D'Amore case (TBL_CELL_DHORIZ):
19232a712daSGarrett D'Amore sz = (*tbl->len)(1, tbl->arg);
19332a712daSGarrett D'Amore if (col->width < sz)
19432a712daSGarrett D'Amore col->width = sz;
19532a712daSGarrett D'Amore break;
19632a712daSGarrett D'Amore case (TBL_CELL_LONG):
19732a712daSGarrett D'Amore /* FALLTHROUGH */
19832a712daSGarrett D'Amore case (TBL_CELL_CENTRE):
19932a712daSGarrett D'Amore /* FALLTHROUGH */
20032a712daSGarrett D'Amore case (TBL_CELL_LEFT):
20132a712daSGarrett D'Amore /* FALLTHROUGH */
20232a712daSGarrett D'Amore case (TBL_CELL_RIGHT):
20332a712daSGarrett D'Amore tblcalc_literal(tbl, col, dp);
20432a712daSGarrett D'Amore break;
20532a712daSGarrett D'Amore case (TBL_CELL_NUMBER):
206*ffb8ebfaSGarrett D'Amore tblcalc_number(tbl, col, opts, dp);
20732a712daSGarrett D'Amore break;
20832a712daSGarrett D'Amore case (TBL_CELL_DOWN):
20932a712daSGarrett D'Amore break;
21032a712daSGarrett D'Amore default:
21132a712daSGarrett D'Amore abort();
21232a712daSGarrett D'Amore /* NOTREACHED */
21332a712daSGarrett D'Amore }
21432a712daSGarrett D'Amore }
21532a712daSGarrett D'Amore
21632a712daSGarrett D'Amore static void
tblcalc_literal(struct rofftbl * tbl,struct roffcol * col,const struct tbl_dat * dp)21732a712daSGarrett D'Amore tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
21832a712daSGarrett D'Amore const struct tbl_dat *dp)
21932a712daSGarrett D'Amore {
22032a712daSGarrett D'Amore size_t sz;
22132a712daSGarrett D'Amore const char *str;
22232a712daSGarrett D'Amore
22332a712daSGarrett D'Amore str = dp->string ? dp->string : "";
22432a712daSGarrett D'Amore sz = (*tbl->slen)(str, tbl->arg);
22532a712daSGarrett D'Amore
22632a712daSGarrett D'Amore if (col->width < sz)
22732a712daSGarrett D'Amore col->width = sz;
22832a712daSGarrett D'Amore }
22932a712daSGarrett D'Amore
23032a712daSGarrett D'Amore static void
tblcalc_number(struct rofftbl * tbl,struct roffcol * col,const struct tbl_opts * opts,const struct tbl_dat * dp)23132a712daSGarrett D'Amore tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
232*ffb8ebfaSGarrett D'Amore const struct tbl_opts *opts, const struct tbl_dat *dp)
23332a712daSGarrett D'Amore {
23432a712daSGarrett D'Amore int i;
23532a712daSGarrett D'Amore size_t sz, psz, ssz, d;
23632a712daSGarrett D'Amore const char *str;
23732a712daSGarrett D'Amore char *cp;
23832a712daSGarrett D'Amore char buf[2];
23932a712daSGarrett D'Amore
24032a712daSGarrett D'Amore /*
24132a712daSGarrett D'Amore * First calculate number width and decimal place (last + 1 for
24232a712daSGarrett D'Amore * non-decimal numbers). If the stored decimal is subsequent to
24332a712daSGarrett D'Amore * ours, make our size longer by that difference
24432a712daSGarrett D'Amore * (right-"shifting"); similarly, if ours is subsequent the
24532a712daSGarrett D'Amore * stored, then extend the stored size by the difference.
24632a712daSGarrett D'Amore * Finally, re-assign the stored values.
24732a712daSGarrett D'Amore */
24832a712daSGarrett D'Amore
24932a712daSGarrett D'Amore str = dp->string ? dp->string : "";
25032a712daSGarrett D'Amore sz = (*tbl->slen)(str, tbl->arg);
25132a712daSGarrett D'Amore
25232a712daSGarrett D'Amore /* FIXME: TBL_DATA_HORIZ et al.? */
25332a712daSGarrett D'Amore
254*ffb8ebfaSGarrett D'Amore buf[0] = opts->decimal;
25532a712daSGarrett D'Amore buf[1] = '\0';
25632a712daSGarrett D'Amore
25732a712daSGarrett D'Amore psz = (*tbl->slen)(buf, tbl->arg);
25832a712daSGarrett D'Amore
259*ffb8ebfaSGarrett D'Amore if (NULL != (cp = strrchr(str, opts->decimal))) {
26032a712daSGarrett D'Amore buf[1] = '\0';
26132a712daSGarrett D'Amore for (ssz = 0, i = 0; cp != &str[i]; i++) {
26232a712daSGarrett D'Amore buf[0] = str[i];
26332a712daSGarrett D'Amore ssz += (*tbl->slen)(buf, tbl->arg);
26432a712daSGarrett D'Amore }
26532a712daSGarrett D'Amore d = ssz + psz;
26632a712daSGarrett D'Amore } else
26732a712daSGarrett D'Amore d = sz + psz;
26832a712daSGarrett D'Amore
26932a712daSGarrett D'Amore /* Adjust the settings for this column. */
27032a712daSGarrett D'Amore
27132a712daSGarrett D'Amore if (col->decimal > d) {
27232a712daSGarrett D'Amore sz += col->decimal - d;
27332a712daSGarrett D'Amore d = col->decimal;
27432a712daSGarrett D'Amore } else
27532a712daSGarrett D'Amore col->width += d - col->decimal;
27632a712daSGarrett D'Amore
27732a712daSGarrett D'Amore if (sz > col->width)
27832a712daSGarrett D'Amore col->width = sz;
27932a712daSGarrett D'Amore if (d > col->decimal)
28032a712daSGarrett D'Amore col->decimal = d;
28132a712daSGarrett D'Amore }
282