]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/out.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r307894, and update
[FreeBSD/FreeBSD.git] / contrib / mdocml / out.c
1 /*      $Id: out.c,v 1.65 2017/06/08 18:11:22 schwarze Exp $ */
2 /*
3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2011, 2014, 2015, 2017 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 <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "mandoc_aux.h"
28 #include "mandoc.h"
29 #include "out.h"
30
31 static  void    tblcalc_data(struct rofftbl *, struct roffcol *,
32                         const struct tbl_opts *, const struct tbl_dat *,
33                         size_t);
34 static  void    tblcalc_literal(struct rofftbl *, struct roffcol *,
35                         const struct tbl_dat *, size_t);
36 static  void    tblcalc_number(struct rofftbl *, struct roffcol *,
37                         const struct tbl_opts *, const struct tbl_dat *);
38
39
40 /*
41  * Parse the *src string and store a scaling unit into *dst.
42  * If the string doesn't specify the unit, use the default.
43  * If no default is specified, fail.
44  * Return a pointer to the byte after the last byte used,
45  * or NULL on total failure.
46  */
47 const char *
48 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
49 {
50         char            *endptr;
51
52         dst->unit = def == SCALE_MAX ? SCALE_BU : def;
53         dst->scale = strtod(src, &endptr);
54         if (endptr == src)
55                 return NULL;
56
57         switch (*endptr++) {
58         case 'c':
59                 dst->unit = SCALE_CM;
60                 break;
61         case 'i':
62                 dst->unit = SCALE_IN;
63                 break;
64         case 'f':
65                 dst->unit = SCALE_FS;
66                 break;
67         case 'M':
68                 dst->unit = SCALE_MM;
69                 break;
70         case 'm':
71                 dst->unit = SCALE_EM;
72                 break;
73         case 'n':
74                 dst->unit = SCALE_EN;
75                 break;
76         case 'P':
77                 dst->unit = SCALE_PC;
78                 break;
79         case 'p':
80                 dst->unit = SCALE_PT;
81                 break;
82         case 'u':
83                 dst->unit = SCALE_BU;
84                 break;
85         case 'v':
86                 dst->unit = SCALE_VS;
87                 break;
88         case '\0':
89                 endptr--;
90                 /* FALLTHROUGH */
91         default:
92                 if (SCALE_MAX == def)
93                         return NULL;
94                 dst->unit = def;
95                 break;
96         }
97         return endptr;
98 }
99
100 /*
101  * Calculate the abstract widths and decimal positions of columns in a
102  * table.  This routine allocates the columns structures then runs over
103  * all rows and cells in the table.  The function pointers in "tbl" are
104  * used for the actual width calculations.
105  */
106 void
107 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
108         size_t totalwidth)
109 {
110         struct roffsu            su;
111         const struct tbl_opts   *opts;
112         const struct tbl_dat    *dp;
113         struct roffcol          *col;
114         size_t                   ewidth, xwidth;
115         int                      spans;
116         int                      icol, maxcol, necol, nxcol, quirkcol;
117
118         /*
119          * Allocate the master column specifiers.  These will hold the
120          * widths and decimal positions for all cells in the column.  It
121          * must be freed and nullified by the caller.
122          */
123
124         assert(NULL == tbl->cols);
125         tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
126             sizeof(struct roffcol));
127         opts = sp->opts;
128
129         for (maxcol = -1; sp; sp = sp->next) {
130                 if (TBL_SPAN_DATA != sp->pos)
131                         continue;
132                 spans = 1;
133                 /*
134                  * Account for the data cells in the layout, matching it
135                  * to data cells in the data section.
136                  */
137                 for (dp = sp->first; dp; dp = dp->next) {
138                         /* Do not used spanned cells in the calculation. */
139                         if (0 < --spans)
140                                 continue;
141                         spans = dp->spans;
142                         if (1 < spans)
143                                 continue;
144                         icol = dp->layout->col;
145                         if (maxcol < icol)
146                                 maxcol = icol;
147                         col = tbl->cols + icol;
148                         col->flags |= dp->layout->flags;
149                         if (dp->layout->flags & TBL_CELL_WIGN)
150                                 continue;
151                         if (dp->layout->wstr != NULL &&
152                             dp->layout->width == 0 &&
153                             a2roffsu(dp->layout->wstr, &su, SCALE_EN)
154                             != NULL)
155                                 dp->layout->width =
156                                     (*tbl->sulen)(&su, tbl->arg);
157                         if (col->width < dp->layout->width)
158                                 col->width = dp->layout->width;
159                         tblcalc_data(tbl, col, opts, dp, dp->block ?
160                             totalwidth / (sp->opts->cols + 1) : 0);
161                 }
162         }
163
164         /*
165          * Count columns to equalize and columns to maximize.
166          * Find maximum width of the columns to equalize.
167          * Find total width of the columns *not* to maximize.
168          */
169
170         necol = nxcol = 0;
171         ewidth = xwidth = 0;
172         for (icol = 0; icol <= maxcol; icol++) {
173                 col = tbl->cols + icol;
174                 if (col->flags & TBL_CELL_EQUAL) {
175                         necol++;
176                         if (ewidth < col->width)
177                                 ewidth = col->width;
178                 }
179                 if (col->flags & TBL_CELL_WMAX)
180                         nxcol++;
181                 else
182                         xwidth += col->width;
183         }
184
185         /*
186          * Equalize columns, if requested for any of them.
187          * Update total width of the columns not to maximize.
188          */
189
190         if (necol) {
191                 for (icol = 0; icol <= maxcol; icol++) {
192                         col = tbl->cols + icol;
193                         if ( ! (col->flags & TBL_CELL_EQUAL))
194                                 continue;
195                         if (col->width == ewidth)
196                                 continue;
197                         if (nxcol && totalwidth)
198                                 xwidth += ewidth - col->width;
199                         col->width = ewidth;
200                 }
201         }
202
203         /*
204          * If there are any columns to maximize, find the total
205          * available width, deducting 3n margins between columns.
206          * Distribute the available width evenly.
207          */
208
209         if (nxcol && totalwidth) {
210                 xwidth += 3*maxcol +
211                     (opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
212                      2 : !!opts->lvert + !!opts->rvert);
213                 if (xwidth >= totalwidth)
214                         return;
215                 xwidth = totalwidth - xwidth;
216
217                 /*
218                  * Emulate a bug in GNU tbl width calculation that
219                  * manifests itself for large numbers of x-columns.
220                  * Emulating it for 5 x-columns gives identical
221                  * behaviour for up to 6 x-columns.
222                  */
223
224                 if (nxcol == 5) {
225                         quirkcol = xwidth % nxcol + 2;
226                         if (quirkcol != 3 && quirkcol != 4)
227                                 quirkcol = -1;
228                 } else
229                         quirkcol = -1;
230
231                 necol = 0;
232                 ewidth = 0;
233                 for (icol = 0; icol <= maxcol; icol++) {
234                         col = tbl->cols + icol;
235                         if ( ! (col->flags & TBL_CELL_WMAX))
236                                 continue;
237                         col->width = (double)xwidth * ++necol / nxcol
238                             - ewidth + 0.4995;
239                         if (necol == quirkcol)
240                                 col->width--;
241                         ewidth += col->width;
242                 }
243         }
244 }
245
246 static void
247 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
248     const struct tbl_opts *opts, const struct tbl_dat *dp, size_t mw)
249 {
250         size_t           sz;
251
252         /* Branch down into data sub-types. */
253
254         switch (dp->layout->pos) {
255         case TBL_CELL_HORIZ:
256         case TBL_CELL_DHORIZ:
257                 sz = (*tbl->len)(1, tbl->arg);
258                 if (col->width < sz)
259                         col->width = sz;
260                 break;
261         case TBL_CELL_LONG:
262         case TBL_CELL_CENTRE:
263         case TBL_CELL_LEFT:
264         case TBL_CELL_RIGHT:
265                 tblcalc_literal(tbl, col, dp, mw);
266                 break;
267         case TBL_CELL_NUMBER:
268                 tblcalc_number(tbl, col, opts, dp);
269                 break;
270         case TBL_CELL_DOWN:
271                 break;
272         default:
273                 abort();
274         }
275 }
276
277 static void
278 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
279     const struct tbl_dat *dp, size_t mw)
280 {
281         const char      *str;   /* Beginning of the first line. */
282         const char      *beg;   /* Beginning of the current line. */
283         char            *end;   /* End of the current line. */
284         size_t           sz;    /* Length of the current line. */
285
286         if (dp->string == NULL || *dp->string == '\0')
287                 return;
288         str = mw ? mandoc_strdup(dp->string) : dp->string;
289         for (beg = str; beg != NULL && *beg != '\0'; beg = end) {
290                 end = mw ? strchr(beg, ' ') : NULL;
291                 if (end != NULL) {
292                         *end++ = '\0';
293                         while (*end == ' ')
294                                 end++;
295                 }
296                 sz = (*tbl->slen)(beg, tbl->arg);
297                 if (col->width < sz)
298                         col->width = sz;
299         }
300         if (mw)
301                 free((void *)str);
302 }
303
304 static void
305 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
306                 const struct tbl_opts *opts, const struct tbl_dat *dp)
307 {
308         int              i;
309         size_t           sz, psz, ssz, d;
310         const char      *str;
311         char            *cp;
312         char             buf[2];
313
314         /*
315          * First calculate number width and decimal place (last + 1 for
316          * non-decimal numbers).  If the stored decimal is subsequent to
317          * ours, make our size longer by that difference
318          * (right-"shifting"); similarly, if ours is subsequent the
319          * stored, then extend the stored size by the difference.
320          * Finally, re-assign the stored values.
321          */
322
323         str = dp->string ? dp->string : "";
324         sz = (*tbl->slen)(str, tbl->arg);
325
326         /* FIXME: TBL_DATA_HORIZ et al.? */
327
328         buf[0] = opts->decimal;
329         buf[1] = '\0';
330
331         psz = (*tbl->slen)(buf, tbl->arg);
332
333         if (NULL != (cp = strrchr(str, opts->decimal))) {
334                 buf[1] = '\0';
335                 for (ssz = 0, i = 0; cp != &str[i]; i++) {
336                         buf[0] = str[i];
337                         ssz += (*tbl->slen)(buf, tbl->arg);
338                 }
339                 d = ssz + psz;
340         } else
341                 d = sz + psz;
342
343         /* Adjust the settings for this column. */
344
345         if (col->decimal > d) {
346                 sz += col->decimal - d;
347                 d = col->decimal;
348         } else
349                 col->width += d - col->decimal;
350
351         if (sz > col->width)
352                 col->width = sz;
353         if (d > col->decimal)
354                 col->decimal = d;
355 }