Commit | Line | Data |
---|---|---|
7888c61d | 1 | /* $Id: out.c,v 1.46 2013/10/05 20:30:05 schwarze Exp $ */ |
80387638 | 2 | /* |
60e1e752 SW |
3 | * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
4 | * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> | |
80387638 SW |
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 *, | |
f88b6c16 | 35 | const struct tbl_opts *, const struct tbl_dat *); |
80387638 SW |
36 | static void tblcalc_literal(struct rofftbl *, struct roffcol *, |
37 | const struct tbl_dat *); | |
38 | static void tblcalc_number(struct rofftbl *, struct roffcol *, | |
f88b6c16 | 39 | const struct tbl_opts *, const struct tbl_dat *); |
80387638 SW |
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) { | |
36342e81 | 72 | if ( ! isdigit((unsigned char)*src)) { |
80387638 SW |
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 | ||
80387638 SW |
135 | /* |
136 | * Calculate the abstract widths and decimal positions of columns in a | |
137 | * table. This routine allocates the columns structures then runs over | |
138 | * all rows and cells in the table. The function pointers in "tbl" are | |
139 | * used for the actual width calculations. | |
140 | */ | |
141 | void | |
142 | tblcalc(struct rofftbl *tbl, const struct tbl_span *sp) | |
143 | { | |
144 | const struct tbl_dat *dp; | |
80387638 | 145 | struct roffcol *col; |
36342e81 | 146 | int spans; |
80387638 SW |
147 | |
148 | /* | |
149 | * Allocate the master column specifiers. These will hold the | |
150 | * widths and decimal positions for all cells in the column. It | |
151 | * must be freed and nullified by the caller. | |
152 | */ | |
153 | ||
154 | assert(NULL == tbl->cols); | |
60e1e752 | 155 | tbl->cols = mandoc_calloc |
f88b6c16 | 156 | ((size_t)sp->opts->cols, sizeof(struct roffcol)); |
80387638 | 157 | |
80387638 SW |
158 | for ( ; sp; sp = sp->next) { |
159 | if (TBL_SPAN_DATA != sp->pos) | |
160 | continue; | |
36342e81 | 161 | spans = 1; |
80387638 SW |
162 | /* |
163 | * Account for the data cells in the layout, matching it | |
164 | * to data cells in the data section. | |
165 | */ | |
166 | for (dp = sp->first; dp; dp = dp->next) { | |
36342e81 SW |
167 | /* Do not used spanned cells in the calculation. */ |
168 | if (0 < --spans) | |
169 | continue; | |
170 | spans = dp->spans; | |
171 | if (1 < spans) | |
172 | continue; | |
60e1e752 | 173 | assert(dp->layout); |
80387638 | 174 | col = &tbl->cols[dp->layout->head->ident]; |
f88b6c16 | 175 | tblcalc_data(tbl, col, sp->opts, dp); |
80387638 SW |
176 | } |
177 | } | |
178 | } | |
179 | ||
180 | static void | |
181 | tblcalc_data(struct rofftbl *tbl, struct roffcol *col, | |
f88b6c16 | 182 | const struct tbl_opts *opts, const struct tbl_dat *dp) |
80387638 SW |
183 | { |
184 | size_t sz; | |
185 | ||
186 | /* Branch down into data sub-types. */ | |
187 | ||
188 | switch (dp->layout->pos) { | |
189 | case (TBL_CELL_HORIZ): | |
190 | /* FALLTHROUGH */ | |
191 | case (TBL_CELL_DHORIZ): | |
192 | sz = (*tbl->len)(1, tbl->arg); | |
193 | if (col->width < sz) | |
194 | col->width = sz; | |
195 | break; | |
196 | case (TBL_CELL_LONG): | |
197 | /* FALLTHROUGH */ | |
198 | case (TBL_CELL_CENTRE): | |
199 | /* FALLTHROUGH */ | |
200 | case (TBL_CELL_LEFT): | |
201 | /* FALLTHROUGH */ | |
202 | case (TBL_CELL_RIGHT): | |
203 | tblcalc_literal(tbl, col, dp); | |
204 | break; | |
205 | case (TBL_CELL_NUMBER): | |
f88b6c16 | 206 | tblcalc_number(tbl, col, opts, dp); |
80387638 | 207 | break; |
60e1e752 SW |
208 | case (TBL_CELL_DOWN): |
209 | break; | |
80387638 SW |
210 | default: |
211 | abort(); | |
212 | /* NOTREACHED */ | |
213 | } | |
214 | } | |
215 | ||
216 | static void | |
217 | tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, | |
218 | const struct tbl_dat *dp) | |
219 | { | |
36342e81 | 220 | size_t sz; |
60e1e752 | 221 | const char *str; |
80387638 | 222 | |
60e1e752 SW |
223 | str = dp->string ? dp->string : ""; |
224 | sz = (*tbl->slen)(str, tbl->arg); | |
225 | ||
80387638 SW |
226 | if (col->width < sz) |
227 | col->width = sz; | |
228 | } | |
229 | ||
230 | static void | |
231 | tblcalc_number(struct rofftbl *tbl, struct roffcol *col, | |
f88b6c16 | 232 | const struct tbl_opts *opts, const struct tbl_dat *dp) |
80387638 SW |
233 | { |
234 | int i; | |
235 | size_t sz, psz, ssz, d; | |
80387638 | 236 | const char *str; |
60e1e752 | 237 | char *cp; |
80387638 SW |
238 | char buf[2]; |
239 | ||
80387638 SW |
240 | /* |
241 | * First calculate number width and decimal place (last + 1 for | |
36342e81 | 242 | * non-decimal numbers). If the stored decimal is subsequent to |
80387638 SW |
243 | * ours, make our size longer by that difference |
244 | * (right-"shifting"); similarly, if ours is subsequent the | |
245 | * stored, then extend the stored size by the difference. | |
246 | * Finally, re-assign the stored values. | |
247 | */ | |
248 | ||
60e1e752 | 249 | str = dp->string ? dp->string : ""; |
80387638 SW |
250 | sz = (*tbl->slen)(str, tbl->arg); |
251 | ||
60e1e752 SW |
252 | /* FIXME: TBL_DATA_HORIZ et al.? */ |
253 | ||
f88b6c16 | 254 | buf[0] = opts->decimal; |
80387638 SW |
255 | buf[1] = '\0'; |
256 | ||
257 | psz = (*tbl->slen)(buf, tbl->arg); | |
258 | ||
f88b6c16 | 259 | if (NULL != (cp = strrchr(str, opts->decimal))) { |
80387638 SW |
260 | buf[1] = '\0'; |
261 | for (ssz = 0, i = 0; cp != &str[i]; i++) { | |
262 | buf[0] = str[i]; | |
263 | ssz += (*tbl->slen)(buf, tbl->arg); | |
264 | } | |
265 | d = ssz + psz; | |
266 | } else | |
267 | d = sz + psz; | |
268 | ||
80387638 SW |
269 | /* Adjust the settings for this column. */ |
270 | ||
271 | if (col->decimal > d) { | |
272 | sz += col->decimal - d; | |
273 | d = col->decimal; | |
274 | } else | |
275 | col->width += d - col->decimal; | |
276 | ||
277 | if (sz > col->width) | |
278 | col->width = sz; | |
279 | if (d > col->decimal) | |
280 | col->decimal = d; | |
281 | } |