]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/tbl_layout.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r305145, and update
[FreeBSD/FreeBSD.git] / contrib / mdocml / tbl_layout.c
1 /*      $Id: tbl_layout.c,v 1.42 2017/06/08 18:11:22 schwarze Exp $ */
2 /*
3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2012, 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 <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26
27 #include "mandoc.h"
28 #include "mandoc_aux.h"
29 #include "libmandoc.h"
30 #include "libroff.h"
31
32 struct  tbl_phrase {
33         char             name;
34         enum tbl_cellt   key;
35 };
36
37 static  const struct tbl_phrase keys[] = {
38         { 'c',           TBL_CELL_CENTRE },
39         { 'r',           TBL_CELL_RIGHT },
40         { 'l',           TBL_CELL_LEFT },
41         { 'n',           TBL_CELL_NUMBER },
42         { 's',           TBL_CELL_SPAN },
43         { 'a',           TBL_CELL_LONG },
44         { '^',           TBL_CELL_DOWN },
45         { '-',           TBL_CELL_HORIZ },
46         { '_',           TBL_CELL_HORIZ },
47         { '=',           TBL_CELL_DHORIZ }
48 };
49
50 #define KEYS_MAX ((int)(sizeof(keys)/sizeof(keys[0])))
51
52 static  void             mods(struct tbl_node *, struct tbl_cell *,
53                                 int, const char *, int *);
54 static  void             cell(struct tbl_node *, struct tbl_row *,
55                                 int, const char *, int *);
56 static  struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
57                                 enum tbl_cellt);
58
59
60 static void
61 mods(struct tbl_node *tbl, struct tbl_cell *cp,
62                 int ln, const char *p, int *pos)
63 {
64         char            *endptr;
65         size_t           sz;
66
67 mod:
68         while (p[*pos] == ' ' || p[*pos] == '\t')
69                 (*pos)++;
70
71         /* Row delimiters and cell specifiers end modifier lists. */
72
73         if (strchr(".,-=^_ACLNRSaclnrs", p[*pos]) != NULL)
74                 return;
75
76         /* Throw away parenthesised expression. */
77
78         if ('(' == p[*pos]) {
79                 (*pos)++;
80                 while (p[*pos] && ')' != p[*pos])
81                         (*pos)++;
82                 if (')' == p[*pos]) {
83                         (*pos)++;
84                         goto mod;
85                 }
86                 mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, tbl->parse,
87                     ln, *pos, NULL);
88                 return;
89         }
90
91         /* Parse numerical spacing from modifier string. */
92
93         if (isdigit((unsigned char)p[*pos])) {
94                 cp->spacing = strtoull(p + *pos, &endptr, 10);
95                 *pos = endptr - p;
96                 goto mod;
97         }
98
99         switch (tolower((unsigned char)p[(*pos)++])) {
100         case 'b':
101                 cp->flags |= TBL_CELL_BOLD;
102                 goto mod;
103         case 'd':
104                 cp->flags |= TBL_CELL_BALIGN;
105                 goto mod;
106         case 'e':
107                 cp->flags |= TBL_CELL_EQUAL;
108                 goto mod;
109         case 'f':
110                 break;
111         case 'i':
112                 cp->flags |= TBL_CELL_ITALIC;
113                 goto mod;
114         case 'm':
115                 mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, tbl->parse,
116                     ln, *pos, "m");
117                 goto mod;
118         case 'p':
119         case 'v':
120                 if (p[*pos] == '-' || p[*pos] == '+')
121                         (*pos)++;
122                 while (isdigit((unsigned char)p[*pos]))
123                         (*pos)++;
124                 goto mod;
125         case 't':
126                 cp->flags |= TBL_CELL_TALIGN;
127                 goto mod;
128         case 'u':
129                 cp->flags |= TBL_CELL_UP;
130                 goto mod;
131         case 'w':
132                 sz = 0;
133                 if (p[*pos] == '(') {
134                         (*pos)++;
135                         while (p[*pos + sz] != '\0' && p[*pos + sz] != ')')
136                                 sz++;
137                 } else
138                         while (isdigit((unsigned char)p[*pos + sz]))
139                                 sz++;
140                 if (sz) {
141                         free(cp->wstr);
142                         cp->wstr = mandoc_strndup(p + *pos, sz);
143                         *pos += sz;
144                         if (p[*pos] == ')')
145                                 (*pos)++;
146                 }
147                 goto mod;
148         case 'x':
149                 cp->flags |= TBL_CELL_WMAX;
150                 goto mod;
151         case 'z':
152                 cp->flags |= TBL_CELL_WIGN;
153                 goto mod;
154         case '|':
155                 if (cp->vert < 2)
156                         cp->vert++;
157                 else
158                         mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
159                             tbl->parse, ln, *pos - 1, NULL);
160                 goto mod;
161         default:
162                 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
163                     ln, *pos - 1, "%c", p[*pos - 1]);
164                 goto mod;
165         }
166
167         /* Ignore parenthised font names for now. */
168
169         if (p[*pos] == '(')
170                 goto mod;
171
172         /* Support only one-character font-names for now. */
173
174         if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) {
175                 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
176                     ln, *pos, "TS %s", p + *pos - 1);
177                 if (p[*pos] != '\0')
178                         (*pos)++;
179                 if (p[*pos] != '\0')
180                         (*pos)++;
181                 goto mod;
182         }
183
184         switch (p[(*pos)++]) {
185         case '3':
186         case 'B':
187                 cp->flags |= TBL_CELL_BOLD;
188                 goto mod;
189         case '2':
190         case 'I':
191                 cp->flags |= TBL_CELL_ITALIC;
192                 goto mod;
193         case '1':
194         case 'R':
195                 goto mod;
196         default:
197                 mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
198                     ln, *pos - 1, "TS f%c", p[*pos - 1]);
199                 goto mod;
200         }
201 }
202
203 static void
204 cell(struct tbl_node *tbl, struct tbl_row *rp,
205                 int ln, const char *p, int *pos)
206 {
207         int              i;
208         enum tbl_cellt   c;
209
210         /* Handle leading vertical lines */
211
212         while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') {
213                 if (p[*pos] == '|') {
214                         if (rp->vert < 2)
215                                 rp->vert++;
216                         else
217                                 mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
218                                     tbl->parse, ln, *pos, NULL);
219                 }
220                 (*pos)++;
221         }
222
223 again:
224         while (p[*pos] == ' ' || p[*pos] == '\t')
225                 (*pos)++;
226
227         if (p[*pos] == '.' || p[*pos] == '\0')
228                 return;
229
230         /* Parse the column position (`c', `l', `r', ...). */
231
232         for (i = 0; i < KEYS_MAX; i++)
233                 if (tolower((unsigned char)p[*pos]) == keys[i].name)
234                         break;
235
236         if (i == KEYS_MAX) {
237                 mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
238                     ln, *pos, "%c", p[*pos]);
239                 (*pos)++;
240                 goto again;
241         }
242         c = keys[i].key;
243
244         /* Special cases of spanners. */
245
246         if (c == TBL_CELL_SPAN) {
247                 if (rp->last == NULL)
248                         mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN,
249                             tbl->parse, ln, *pos, NULL);
250                 else if (rp->last->pos == TBL_CELL_HORIZ ||
251                     rp->last->pos == TBL_CELL_DHORIZ)
252                         c = rp->last->pos;
253         } else if (c == TBL_CELL_DOWN && rp == tbl->first_row)
254                 mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN,
255                     tbl->parse, ln, *pos, NULL);
256
257         (*pos)++;
258
259         /* Allocate cell then parse its modifiers. */
260
261         mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos);
262 }
263
264 void
265 tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos)
266 {
267         struct tbl_row  *rp;
268
269         rp = NULL;
270         for (;;) {
271                 /* Skip whitespace before and after each cell. */
272
273                 while (p[pos] == ' ' || p[pos] == '\t')
274                         pos++;
275
276                 switch (p[pos]) {
277                 case ',':  /* Next row on this input line. */
278                         pos++;
279                         rp = NULL;
280                         continue;
281                 case '\0':  /* Next row on next input line. */
282                         return;
283                 case '.':  /* End of layout. */
284                         pos++;
285                         tbl->part = TBL_PART_DATA;
286
287                         /*
288                          * When the layout is completely empty,
289                          * default to one left-justified column.
290                          */
291
292                         if (tbl->first_row == NULL) {
293                                 tbl->first_row = tbl->last_row =
294                                     mandoc_calloc(1, sizeof(*rp));
295                         }
296                         if (tbl->first_row->first == NULL) {
297                                 mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
298                                     tbl->parse, ln, pos, NULL);
299                                 cell_alloc(tbl, tbl->first_row,
300                                     TBL_CELL_LEFT);
301                                 return;
302                         }
303
304                         /*
305                          * Search for the widest line
306                          * along the left and right margins.
307                          */
308
309                         for (rp = tbl->first_row; rp; rp = rp->next) {
310                                 if (tbl->opts.lvert < rp->vert)
311                                         tbl->opts.lvert = rp->vert;
312                                 if (rp->last != NULL &&
313                                     rp->last->col + 1 == tbl->opts.cols &&
314                                     tbl->opts.rvert < rp->last->vert)
315                                         tbl->opts.rvert = rp->last->vert;
316
317                                 /* If the last line is empty, drop it. */
318
319                                 if (rp->next != NULL &&
320                                     rp->next->first == NULL) {
321                                         free(rp->next);
322                                         rp->next = NULL;
323                                         tbl->last_row = rp;
324                                 }
325                         }
326                         return;
327                 default:  /* Cell. */
328                         break;
329                 }
330
331                 /*
332                  * If the last line had at least one cell,
333                  * start a new one; otherwise, continue it.
334                  */
335
336                 if (rp == NULL) {
337                         if (tbl->last_row == NULL ||
338                             tbl->last_row->first != NULL) {
339                                 rp = mandoc_calloc(1, sizeof(*rp));
340                                 if (tbl->last_row)
341                                         tbl->last_row->next = rp;
342                                 else
343                                         tbl->first_row = rp;
344                                 tbl->last_row = rp;
345                         } else
346                                 rp = tbl->last_row;
347                 }
348                 cell(tbl, rp, ln, p, &pos);
349         }
350 }
351
352 static struct tbl_cell *
353 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
354 {
355         struct tbl_cell *p, *pp;
356
357         p = mandoc_calloc(1, sizeof(*p));
358         p->pos = pos;
359
360         if ((pp = rp->last) != NULL) {
361                 pp->next = p;
362                 p->col = pp->col + 1;
363         } else
364                 rp->first = p;
365         rp->last = p;
366
367         if (tbl->opts.cols <= p->col)
368                 tbl->opts.cols = p->col + 1;
369
370         return p;
371 }