]> git.cameronkatri.com Git - mandoc.git/blob - out.c
Remember to NULL-ify new keys/vals.
[mandoc.git] / out.c
1 /* $Id: out.c,v 1.40 2011/04/09 15:29:40 kristaps Exp $ */
2 /*
3 * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
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 *,
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 *);
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) {
72 if ( ! isdigit((u_char)*src)) {
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
135
136 /*
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.
140 */
141 void
142 time2a(time_t t, char *dst, size_t sz)
143 {
144 struct tm tm;
145 char buf[5];
146 char *p;
147 size_t nsz;
148
149 assert(sz > 1);
150 localtime_r(&t, &tm);
151
152 p = dst;
153 nsz = 0;
154
155 dst[0] = '\0';
156
157 if (0 == (nsz = strftime(p, sz, "%B ", &tm)))
158 return;
159
160 p += (int)nsz;
161 sz -= nsz;
162
163 if (0 == strftime(buf, sizeof(buf), "%e, ", &tm))
164 return;
165
166 nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz);
167
168 if (nsz >= sz)
169 return;
170
171 p += (int)nsz;
172 sz -= nsz;
173
174 (void)strftime(p, sz, "%Y", &tm);
175 }
176
177 /*
178 * Calculate the abstract widths and decimal positions of columns in a
179 * table. This routine allocates the columns structures then runs over
180 * all rows and cells in the table. The function pointers in "tbl" are
181 * used for the actual width calculations.
182 */
183 void
184 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
185 {
186 const struct tbl_dat *dp;
187 const struct tbl_head *hp;
188 struct roffcol *col;
189
190 /*
191 * Allocate the master column specifiers. These will hold the
192 * widths and decimal positions for all cells in the column. It
193 * must be freed and nullified by the caller.
194 */
195
196 assert(NULL == tbl->cols);
197 tbl->cols = mandoc_calloc
198 ((size_t)sp->tbl->cols, sizeof(struct roffcol));
199
200 hp = sp->head;
201
202 for ( ; sp; sp = sp->next) {
203 if (TBL_SPAN_DATA != sp->pos)
204 continue;
205 /*
206 * Account for the data cells in the layout, matching it
207 * to data cells in the data section.
208 */
209 for (dp = sp->first; dp; dp = dp->next) {
210 assert(dp->layout);
211 col = &tbl->cols[dp->layout->head->ident];
212 tblcalc_data(tbl, col, sp->tbl, dp);
213 }
214 }
215
216 /*
217 * Calculate width of the spanners. These get one space for a
218 * vertical line, two for a double-vertical line.
219 */
220
221 for ( ; hp; hp = hp->next) {
222 col = &tbl->cols[hp->ident];
223 switch (hp->pos) {
224 case (TBL_HEAD_VERT):
225 col->width = (*tbl->len)(1, tbl->arg);
226 break;
227 case (TBL_HEAD_DVERT):
228 col->width = (*tbl->len)(2, tbl->arg);
229 break;
230 default:
231 break;
232 }
233 }
234 }
235
236 static void
237 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
238 const struct tbl *tp, const struct tbl_dat *dp)
239 {
240 size_t sz;
241
242 /* Branch down into data sub-types. */
243
244 switch (dp->layout->pos) {
245 case (TBL_CELL_HORIZ):
246 /* FALLTHROUGH */
247 case (TBL_CELL_DHORIZ):
248 sz = (*tbl->len)(1, tbl->arg);
249 if (col->width < sz)
250 col->width = sz;
251 break;
252 case (TBL_CELL_LONG):
253 /* FALLTHROUGH */
254 case (TBL_CELL_CENTRE):
255 /* FALLTHROUGH */
256 case (TBL_CELL_LEFT):
257 /* FALLTHROUGH */
258 case (TBL_CELL_RIGHT):
259 tblcalc_literal(tbl, col, dp);
260 break;
261 case (TBL_CELL_NUMBER):
262 tblcalc_number(tbl, col, tp, dp);
263 break;
264 case (TBL_CELL_DOWN):
265 break;
266 default:
267 abort();
268 /* NOTREACHED */
269 }
270 }
271
272 static void
273 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
274 const struct tbl_dat *dp)
275 {
276 size_t sz, bufsz, spsz;
277 const char *str;
278
279 /*
280 * Calculate our width and use the spacing, with a minimum
281 * spacing dictated by position (centre, e.g,. gets a space on
282 * either side, while right/left get a single adjacent space).
283 */
284
285 bufsz = spsz = 0;
286 str = dp->string ? dp->string : "";
287 sz = (*tbl->slen)(str, tbl->arg);
288
289 /* FIXME: TBL_DATA_HORIZ et al.? */
290
291 assert(dp->layout);
292 switch (dp->layout->pos) {
293 case (TBL_CELL_LONG):
294 /* FALLTHROUGH */
295 case (TBL_CELL_CENTRE):
296 bufsz = (*tbl->len)(1, tbl->arg);
297 break;
298 default:
299 bufsz = (*tbl->len)(1, tbl->arg);
300 break;
301 }
302
303 if (dp->layout->spacing) {
304 spsz = (*tbl->len)(dp->layout->spacing, tbl->arg);
305 bufsz = bufsz > spsz ? bufsz : spsz;
306 }
307
308 sz += bufsz;
309 if (col->width < sz)
310 col->width = sz;
311 }
312
313 static void
314 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
315 const struct tbl *tp, const struct tbl_dat *dp)
316 {
317 int i;
318 size_t sz, psz, ssz, d;
319 const char *str;
320 char *cp;
321 char buf[2];
322
323 /*
324 * First calculate number width and decimal place (last + 1 for
325 * no-decimal numbers). If the stored decimal is subsequent
326 * ours, make our size longer by that difference
327 * (right-"shifting"); similarly, if ours is subsequent the
328 * stored, then extend the stored size by the difference.
329 * Finally, re-assign the stored values.
330 */
331
332 str = dp->string ? dp->string : "";
333 sz = (*tbl->slen)(str, tbl->arg);
334
335 /* FIXME: TBL_DATA_HORIZ et al.? */
336
337 buf[0] = tp->decimal;
338 buf[1] = '\0';
339
340 psz = (*tbl->slen)(buf, tbl->arg);
341
342 if (NULL != (cp = strrchr(str, tp->decimal))) {
343 buf[1] = '\0';
344 for (ssz = 0, i = 0; cp != &str[i]; i++) {
345 buf[0] = str[i];
346 ssz += (*tbl->slen)(buf, tbl->arg);
347 }
348 d = ssz + psz;
349 } else
350 d = sz + psz;
351
352 /* Padding. */
353
354 sz += (*tbl->len)(2, tbl->arg);
355 d += (*tbl->len)(1, tbl->arg);
356
357 /* Adjust the settings for this column. */
358
359 if (col->decimal > d) {
360 sz += col->decimal - d;
361 d = col->decimal;
362 } else
363 col->width += d - col->decimal;
364
365 if (sz > col->width)
366 col->width = sz;
367 if (d > col->decimal)
368 col->decimal = d;
369
370 /* Adjust for stipulated width. */
371
372 if (col->width < dp->layout->spacing)
373 col->width = dp->layout->spacing;
374 }
375
376