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