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