1*0a6a1f1dSLionel Sambuc /* Id: out.c,v 1.46 2013/10/05 20:30:05 schwarze Exp */
2d65f6f70SBen Gras /*
392395e9cSLionel Sambuc * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
492395e9cSLionel Sambuc * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
5d65f6f70SBen Gras *
6d65f6f70SBen Gras * Permission to use, copy, modify, and distribute this software for any
7d65f6f70SBen Gras * purpose with or without fee is hereby granted, provided that the above
8d65f6f70SBen Gras * copyright notice and this permission notice appear in all copies.
9d65f6f70SBen Gras *
10d65f6f70SBen Gras * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d65f6f70SBen Gras * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d65f6f70SBen Gras * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d65f6f70SBen Gras * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d65f6f70SBen Gras * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d65f6f70SBen Gras * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d65f6f70SBen Gras * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d65f6f70SBen Gras */
18d65f6f70SBen Gras #ifdef HAVE_CONFIG_H
19d65f6f70SBen Gras #include "config.h"
20d65f6f70SBen Gras #endif
21d65f6f70SBen Gras
22d65f6f70SBen Gras #include <sys/types.h>
23d65f6f70SBen Gras
24d65f6f70SBen Gras #include <assert.h>
25d65f6f70SBen Gras #include <ctype.h>
26d65f6f70SBen Gras #include <stdio.h>
27d65f6f70SBen Gras #include <stdlib.h>
28d65f6f70SBen Gras #include <string.h>
29d65f6f70SBen Gras #include <time.h>
30d65f6f70SBen Gras
31d65f6f70SBen Gras #include "mandoc.h"
32d65f6f70SBen Gras #include "out.h"
33d65f6f70SBen Gras
34d65f6f70SBen Gras static void tblcalc_data(struct rofftbl *, struct roffcol *,
35*0a6a1f1dSLionel Sambuc const struct tbl_opts *, const struct tbl_dat *);
36d65f6f70SBen Gras static void tblcalc_literal(struct rofftbl *, struct roffcol *,
37d65f6f70SBen Gras const struct tbl_dat *);
38d65f6f70SBen Gras static void tblcalc_number(struct rofftbl *, struct roffcol *,
39*0a6a1f1dSLionel Sambuc const struct tbl_opts *, const struct tbl_dat *);
40d65f6f70SBen Gras
41d65f6f70SBen Gras /*
42d65f6f70SBen Gras * Convert a `scaling unit' to a consistent form, or fail. Scaling
43d65f6f70SBen Gras * units are documented in groff.7, mdoc.7, man.7.
44d65f6f70SBen Gras */
45d65f6f70SBen Gras int
a2roffsu(const char * src,struct roffsu * dst,enum roffscale def)46d65f6f70SBen Gras a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
47d65f6f70SBen Gras {
48d65f6f70SBen Gras char buf[BUFSIZ], hasd;
49d65f6f70SBen Gras int i;
50d65f6f70SBen Gras enum roffscale unit;
51d65f6f70SBen Gras
52d65f6f70SBen Gras if ('\0' == *src)
53d65f6f70SBen Gras return(0);
54d65f6f70SBen Gras
55d65f6f70SBen Gras i = hasd = 0;
56d65f6f70SBen Gras
57d65f6f70SBen Gras switch (*src) {
58d65f6f70SBen Gras case ('+'):
59d65f6f70SBen Gras src++;
60d65f6f70SBen Gras break;
61d65f6f70SBen Gras case ('-'):
62d65f6f70SBen Gras buf[i++] = *src++;
63d65f6f70SBen Gras break;
64d65f6f70SBen Gras default:
65d65f6f70SBen Gras break;
66d65f6f70SBen Gras }
67d65f6f70SBen Gras
68d65f6f70SBen Gras if ('\0' == *src)
69d65f6f70SBen Gras return(0);
70d65f6f70SBen Gras
71d65f6f70SBen Gras while (i < BUFSIZ) {
7292395e9cSLionel Sambuc if ( ! isdigit((unsigned char)*src)) {
73d65f6f70SBen Gras if ('.' != *src)
74d65f6f70SBen Gras break;
75d65f6f70SBen Gras else if (hasd)
76d65f6f70SBen Gras break;
77d65f6f70SBen Gras else
78d65f6f70SBen Gras hasd = 1;
79d65f6f70SBen Gras }
80d65f6f70SBen Gras buf[i++] = *src++;
81d65f6f70SBen Gras }
82d65f6f70SBen Gras
83d65f6f70SBen Gras if (BUFSIZ == i || (*src && *(src + 1)))
84d65f6f70SBen Gras return(0);
85d65f6f70SBen Gras
86d65f6f70SBen Gras buf[i] = '\0';
87d65f6f70SBen Gras
88d65f6f70SBen Gras switch (*src) {
89d65f6f70SBen Gras case ('c'):
90d65f6f70SBen Gras unit = SCALE_CM;
91d65f6f70SBen Gras break;
92d65f6f70SBen Gras case ('i'):
93d65f6f70SBen Gras unit = SCALE_IN;
94d65f6f70SBen Gras break;
95d65f6f70SBen Gras case ('P'):
96d65f6f70SBen Gras unit = SCALE_PC;
97d65f6f70SBen Gras break;
98d65f6f70SBen Gras case ('p'):
99d65f6f70SBen Gras unit = SCALE_PT;
100d65f6f70SBen Gras break;
101d65f6f70SBen Gras case ('f'):
102d65f6f70SBen Gras unit = SCALE_FS;
103d65f6f70SBen Gras break;
104d65f6f70SBen Gras case ('v'):
105d65f6f70SBen Gras unit = SCALE_VS;
106d65f6f70SBen Gras break;
107d65f6f70SBen Gras case ('m'):
108d65f6f70SBen Gras unit = SCALE_EM;
109d65f6f70SBen Gras break;
110d65f6f70SBen Gras case ('\0'):
111d65f6f70SBen Gras if (SCALE_MAX == def)
112d65f6f70SBen Gras return(0);
113d65f6f70SBen Gras unit = SCALE_BU;
114d65f6f70SBen Gras break;
115d65f6f70SBen Gras case ('u'):
116d65f6f70SBen Gras unit = SCALE_BU;
117d65f6f70SBen Gras break;
118d65f6f70SBen Gras case ('M'):
119d65f6f70SBen Gras unit = SCALE_MM;
120d65f6f70SBen Gras break;
121d65f6f70SBen Gras case ('n'):
122d65f6f70SBen Gras unit = SCALE_EN;
123d65f6f70SBen Gras break;
124d65f6f70SBen Gras default:
125d65f6f70SBen Gras return(0);
126d65f6f70SBen Gras }
127d65f6f70SBen Gras
128d65f6f70SBen Gras /* FIXME: do this in the caller. */
129d65f6f70SBen Gras if ((dst->scale = atof(buf)) < 0)
130d65f6f70SBen Gras dst->scale = 0;
131d65f6f70SBen Gras dst->unit = unit;
132d65f6f70SBen Gras return(1);
133d65f6f70SBen Gras }
134d65f6f70SBen Gras
135d65f6f70SBen Gras /*
136d65f6f70SBen Gras * Calculate the abstract widths and decimal positions of columns in a
137d65f6f70SBen Gras * table. This routine allocates the columns structures then runs over
138d65f6f70SBen Gras * all rows and cells in the table. The function pointers in "tbl" are
139d65f6f70SBen Gras * used for the actual width calculations.
140d65f6f70SBen Gras */
141d65f6f70SBen Gras void
tblcalc(struct rofftbl * tbl,const struct tbl_span * sp)142d65f6f70SBen Gras tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
143d65f6f70SBen Gras {
144d65f6f70SBen Gras const struct tbl_dat *dp;
145d65f6f70SBen Gras struct roffcol *col;
14692395e9cSLionel Sambuc int spans;
147d65f6f70SBen Gras
148d65f6f70SBen Gras /*
149d65f6f70SBen Gras * Allocate the master column specifiers. These will hold the
150d65f6f70SBen Gras * widths and decimal positions for all cells in the column. It
151d65f6f70SBen Gras * must be freed and nullified by the caller.
152d65f6f70SBen Gras */
153d65f6f70SBen Gras
154d65f6f70SBen Gras assert(NULL == tbl->cols);
15592395e9cSLionel Sambuc tbl->cols = mandoc_calloc
156*0a6a1f1dSLionel Sambuc ((size_t)sp->opts->cols, sizeof(struct roffcol));
157d65f6f70SBen Gras
158d65f6f70SBen Gras for ( ; sp; sp = sp->next) {
159d65f6f70SBen Gras if (TBL_SPAN_DATA != sp->pos)
160d65f6f70SBen Gras continue;
16192395e9cSLionel Sambuc spans = 1;
162d65f6f70SBen Gras /*
163d65f6f70SBen Gras * Account for the data cells in the layout, matching it
164d65f6f70SBen Gras * to data cells in the data section.
165d65f6f70SBen Gras */
166d65f6f70SBen Gras for (dp = sp->first; dp; dp = dp->next) {
16792395e9cSLionel Sambuc /* Do not used spanned cells in the calculation. */
16892395e9cSLionel Sambuc if (0 < --spans)
169d65f6f70SBen Gras continue;
17092395e9cSLionel Sambuc spans = dp->spans;
17192395e9cSLionel Sambuc if (1 < spans)
17292395e9cSLionel Sambuc continue;
17392395e9cSLionel Sambuc assert(dp->layout);
174d65f6f70SBen Gras col = &tbl->cols[dp->layout->head->ident];
175*0a6a1f1dSLionel Sambuc tblcalc_data(tbl, col, sp->opts, dp);
176d65f6f70SBen Gras }
177d65f6f70SBen Gras }
178d65f6f70SBen Gras }
179d65f6f70SBen Gras
180d65f6f70SBen Gras static void
tblcalc_data(struct rofftbl * tbl,struct roffcol * col,const struct tbl_opts * opts,const struct tbl_dat * dp)181d65f6f70SBen Gras tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
182*0a6a1f1dSLionel Sambuc const struct tbl_opts *opts, const struct tbl_dat *dp)
183d65f6f70SBen Gras {
184d65f6f70SBen Gras size_t sz;
185d65f6f70SBen Gras
186d65f6f70SBen Gras /* Branch down into data sub-types. */
187d65f6f70SBen Gras
188d65f6f70SBen Gras switch (dp->layout->pos) {
189d65f6f70SBen Gras case (TBL_CELL_HORIZ):
190d65f6f70SBen Gras /* FALLTHROUGH */
191d65f6f70SBen Gras case (TBL_CELL_DHORIZ):
192d65f6f70SBen Gras sz = (*tbl->len)(1, tbl->arg);
193d65f6f70SBen Gras if (col->width < sz)
194d65f6f70SBen Gras col->width = sz;
195d65f6f70SBen Gras break;
196d65f6f70SBen Gras case (TBL_CELL_LONG):
197d65f6f70SBen Gras /* FALLTHROUGH */
198d65f6f70SBen Gras case (TBL_CELL_CENTRE):
199d65f6f70SBen Gras /* FALLTHROUGH */
200d65f6f70SBen Gras case (TBL_CELL_LEFT):
201d65f6f70SBen Gras /* FALLTHROUGH */
202d65f6f70SBen Gras case (TBL_CELL_RIGHT):
203d65f6f70SBen Gras tblcalc_literal(tbl, col, dp);
204d65f6f70SBen Gras break;
205d65f6f70SBen Gras case (TBL_CELL_NUMBER):
206*0a6a1f1dSLionel Sambuc tblcalc_number(tbl, col, opts, dp);
207d65f6f70SBen Gras break;
20892395e9cSLionel Sambuc case (TBL_CELL_DOWN):
20992395e9cSLionel Sambuc break;
210d65f6f70SBen Gras default:
211d65f6f70SBen Gras abort();
212d65f6f70SBen Gras /* NOTREACHED */
213d65f6f70SBen Gras }
214d65f6f70SBen Gras }
215d65f6f70SBen Gras
216d65f6f70SBen Gras static void
tblcalc_literal(struct rofftbl * tbl,struct roffcol * col,const struct tbl_dat * dp)217d65f6f70SBen Gras tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
218d65f6f70SBen Gras const struct tbl_dat *dp)
219d65f6f70SBen Gras {
22092395e9cSLionel Sambuc size_t sz;
22192395e9cSLionel Sambuc const char *str;
222d65f6f70SBen Gras
22392395e9cSLionel Sambuc str = dp->string ? dp->string : "";
22492395e9cSLionel Sambuc sz = (*tbl->slen)(str, tbl->arg);
225d65f6f70SBen Gras
226d65f6f70SBen Gras if (col->width < sz)
227d65f6f70SBen Gras col->width = sz;
228d65f6f70SBen Gras }
229d65f6f70SBen Gras
230d65f6f70SBen Gras static void
tblcalc_number(struct rofftbl * tbl,struct roffcol * col,const struct tbl_opts * opts,const struct tbl_dat * dp)231d65f6f70SBen Gras tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
232*0a6a1f1dSLionel Sambuc const struct tbl_opts *opts, const struct tbl_dat *dp)
233d65f6f70SBen Gras {
234d65f6f70SBen Gras int i;
235d65f6f70SBen Gras size_t sz, psz, ssz, d;
236d65f6f70SBen Gras const char *str;
23792395e9cSLionel Sambuc char *cp;
238d65f6f70SBen Gras char buf[2];
239d65f6f70SBen Gras
240d65f6f70SBen Gras /*
241d65f6f70SBen Gras * First calculate number width and decimal place (last + 1 for
24292395e9cSLionel Sambuc * non-decimal numbers). If the stored decimal is subsequent to
243d65f6f70SBen Gras * ours, make our size longer by that difference
244d65f6f70SBen Gras * (right-"shifting"); similarly, if ours is subsequent the
245d65f6f70SBen Gras * stored, then extend the stored size by the difference.
246d65f6f70SBen Gras * Finally, re-assign the stored values.
247d65f6f70SBen Gras */
248d65f6f70SBen Gras
24992395e9cSLionel Sambuc str = dp->string ? dp->string : "";
250d65f6f70SBen Gras sz = (*tbl->slen)(str, tbl->arg);
251d65f6f70SBen Gras
25292395e9cSLionel Sambuc /* FIXME: TBL_DATA_HORIZ et al.? */
25392395e9cSLionel Sambuc
254*0a6a1f1dSLionel Sambuc buf[0] = opts->decimal;
255d65f6f70SBen Gras buf[1] = '\0';
256d65f6f70SBen Gras
257d65f6f70SBen Gras psz = (*tbl->slen)(buf, tbl->arg);
258d65f6f70SBen Gras
259*0a6a1f1dSLionel Sambuc if (NULL != (cp = strrchr(str, opts->decimal))) {
260d65f6f70SBen Gras buf[1] = '\0';
261d65f6f70SBen Gras for (ssz = 0, i = 0; cp != &str[i]; i++) {
262d65f6f70SBen Gras buf[0] = str[i];
263d65f6f70SBen Gras ssz += (*tbl->slen)(buf, tbl->arg);
264d65f6f70SBen Gras }
265d65f6f70SBen Gras d = ssz + psz;
266d65f6f70SBen Gras } else
267d65f6f70SBen Gras d = sz + psz;
268d65f6f70SBen Gras
269d65f6f70SBen Gras /* Adjust the settings for this column. */
270d65f6f70SBen Gras
271d65f6f70SBen Gras if (col->decimal > d) {
272d65f6f70SBen Gras sz += col->decimal - d;
273d65f6f70SBen Gras d = col->decimal;
274d65f6f70SBen Gras } else
275d65f6f70SBen Gras col->width += d - col->decimal;
276d65f6f70SBen Gras
277d65f6f70SBen Gras if (sz > col->width)
278d65f6f70SBen Gras col->width = sz;
279d65f6f70SBen Gras if (d > col->decimal)
280d65f6f70SBen Gras col->decimal = d;
281d65f6f70SBen Gras }
282