Import mdocml-1.11.3
[dragonfly.git] / contrib / mdocml / 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