]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/out.c
Reintegrate head revisions r273096-r277147
[FreeBSD/FreeBSD.git] / contrib / mdocml / out.c
1 /*      $Id: out.c,v 1.54 2014/12/04 02:05:42 schwarze Exp $ */
2 /*
3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2011, 2014 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 #include "config.h"
19
20 #include <sys/types.h>
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28
29 #include "mandoc_aux.h"
30 #include "mandoc.h"
31 #include "out.h"
32
33 static  void    tblcalc_data(struct rofftbl *, struct roffcol *,
34                         const struct tbl_opts *, const struct tbl_dat *);
35 static  void    tblcalc_literal(struct rofftbl *, struct roffcol *,
36                         const struct tbl_dat *);
37 static  void    tblcalc_number(struct rofftbl *, struct roffcol *,
38                         const struct tbl_opts *, const struct tbl_dat *);
39
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((unsigned 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 = def;
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.0)
130                 dst->scale = 0.0;
131         dst->unit = unit;
132         return(1);
133 }
134
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         size_t totalwidth)
144 {
145         const struct tbl_dat    *dp;
146         struct roffcol          *col;
147         size_t                   ewidth, xwidth;
148         int                      spans;
149         int                      icol, maxcol, necol, nxcol;
150
151         /*
152          * Allocate the master column specifiers.  These will hold the
153          * widths and decimal positions for all cells in the column.  It
154          * must be freed and nullified by the caller.
155          */
156
157         assert(NULL == tbl->cols);
158         tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
159             sizeof(struct roffcol));
160
161         for (maxcol = -1; sp; sp = sp->next) {
162                 if (TBL_SPAN_DATA != sp->pos)
163                         continue;
164                 spans = 1;
165                 /*
166                  * Account for the data cells in the layout, matching it
167                  * to data cells in the data section.
168                  */
169                 for (dp = sp->first; dp; dp = dp->next) {
170                         /* Do not used spanned cells in the calculation. */
171                         if (0 < --spans)
172                                 continue;
173                         spans = dp->spans;
174                         if (1 < spans)
175                                 continue;
176                         icol = dp->layout->head->ident;
177                         if (maxcol < icol)
178                                 maxcol = icol;
179                         col = tbl->cols + icol;
180                         col->flags |= dp->layout->flags;
181                         if (dp->layout->flags & TBL_CELL_WIGN)
182                                 continue;
183                         tblcalc_data(tbl, col, sp->opts, dp);
184                 }
185         }
186
187         /*
188          * Count columns to equalize and columns to maximize.
189          * Find maximum width of the columns to equalize.
190          * Find total width of the columns *not* to maximize.
191          */
192
193         necol = nxcol = 0;
194         ewidth = xwidth = 0;
195         for (icol = 0; icol <= maxcol; icol++) {
196                 col = tbl->cols + icol;
197                 if (col->flags & TBL_CELL_EQUAL) {
198                         necol++;
199                         if (ewidth < col->width)
200                                 ewidth = col->width;
201                 }
202                 if (col->flags & TBL_CELL_WMAX)
203                         nxcol++;
204                 else
205                         xwidth += col->width;
206         }
207
208         /*
209          * Equalize columns, if requested for any of them.
210          * Update total width of the columns not to maximize.
211          */
212
213         if (necol) {
214                 for (icol = 0; icol <= maxcol; icol++) {
215                         col = tbl->cols + icol;
216                         if ( ! (col->flags & TBL_CELL_EQUAL))
217                                 continue;
218                         if (col->width == ewidth)
219                                 continue;
220                         if (nxcol && totalwidth)
221                                 xwidth += ewidth - col->width;
222                         col->width = ewidth;
223                 }
224         }
225
226         /*
227          * If there are any columns to maximize, find the total
228          * available width, deducting 3n margins between columns.
229          * Distribute the available width evenly.
230          */
231
232         if (nxcol && totalwidth) {
233                 xwidth = totalwidth - 3*maxcol - xwidth;
234                 for (icol = 0; icol <= maxcol; icol++) {
235                         col = tbl->cols + icol;
236                         if ( ! (col->flags & TBL_CELL_WMAX))
237                                 continue;
238                         col->width = xwidth / nxcol--;
239                         xwidth -= col->width;
240                 }
241         }
242 }
243
244 static void
245 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
246                 const struct tbl_opts *opts, const struct tbl_dat *dp)
247 {
248         size_t           sz;
249
250         /* Branch down into data sub-types. */
251
252         switch (dp->layout->pos) {
253         case TBL_CELL_HORIZ:
254                 /* FALLTHROUGH */
255         case TBL_CELL_DHORIZ:
256                 sz = (*tbl->len)(1, tbl->arg);
257                 if (col->width < sz)
258                         col->width = sz;
259                 break;
260         case TBL_CELL_LONG:
261                 /* FALLTHROUGH */
262         case TBL_CELL_CENTRE:
263                 /* FALLTHROUGH */
264         case TBL_CELL_LEFT:
265                 /* FALLTHROUGH */
266         case TBL_CELL_RIGHT:
267                 tblcalc_literal(tbl, col, dp);
268                 break;
269         case TBL_CELL_NUMBER:
270                 tblcalc_number(tbl, col, opts, dp);
271                 break;
272         case TBL_CELL_DOWN:
273                 break;
274         default:
275                 abort();
276                 /* NOTREACHED */
277         }
278 }
279
280 static void
281 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
282                 const struct tbl_dat *dp)
283 {
284         size_t           sz;
285         const char      *str;
286
287         str = dp->string ? dp->string : "";
288         sz = (*tbl->slen)(str, tbl->arg);
289
290         if (col->width < sz)
291                 col->width = sz;
292 }
293
294 static void
295 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
296                 const struct tbl_opts *opts, const struct tbl_dat *dp)
297 {
298         int              i;
299         size_t           sz, psz, ssz, d;
300         const char      *str;
301         char            *cp;
302         char             buf[2];
303
304         /*
305          * First calculate number width and decimal place (last + 1 for
306          * non-decimal numbers).  If the stored decimal is subsequent to
307          * ours, make our size longer by that difference
308          * (right-"shifting"); similarly, if ours is subsequent the
309          * stored, then extend the stored size by the difference.
310          * Finally, re-assign the stored values.
311          */
312
313         str = dp->string ? dp->string : "";
314         sz = (*tbl->slen)(str, tbl->arg);
315
316         /* FIXME: TBL_DATA_HORIZ et al.? */
317
318         buf[0] = opts->decimal;
319         buf[1] = '\0';
320
321         psz = (*tbl->slen)(buf, tbl->arg);
322
323         if (NULL != (cp = strrchr(str, opts->decimal))) {
324                 buf[1] = '\0';
325                 for (ssz = 0, i = 0; cp != &str[i]; i++) {
326                         buf[0] = str[i];
327                         ssz += (*tbl->slen)(buf, tbl->arg);
328                 }
329                 d = ssz + psz;
330         } else
331                 d = sz + psz;
332
333         /* Adjust the settings for this column. */
334
335         if (col->decimal > d) {
336                 sz += col->decimal - d;
337                 d = col->decimal;
338         } else
339                 col->width += d - col->decimal;
340
341         if (sz > col->width)
342                 col->width = sz;
343         if (d > col->decimal)
344                 col->decimal = d;
345 }