Merge branch 'vendor/GCC50'
[dragonfly.git] / contrib / mdocml / tbl_layout.c
1 /*      $Id: tbl_layout.c,v 1.26 2014/04/20 16:46:05 schwarze Exp $ */
2 /*
3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2012, 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 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
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 /*
38  * FIXME: we can make this parse a lot nicer by, when an error is
39  * encountered in a layout key, bailing to the next key (i.e. to the
40  * next whitespace then continuing).
41  */
42
43 #define KEYS_MAX         11
44
45 static  const struct tbl_phrase keys[KEYS_MAX] = {
46         { 'c',           TBL_CELL_CENTRE },
47         { 'r',           TBL_CELL_RIGHT },
48         { 'l',           TBL_CELL_LEFT },
49         { 'n',           TBL_CELL_NUMBER },
50         { 's',           TBL_CELL_SPAN },
51         { 'a',           TBL_CELL_LONG },
52         { '^',           TBL_CELL_DOWN },
53         { '-',           TBL_CELL_HORIZ },
54         { '_',           TBL_CELL_HORIZ },
55         { '=',           TBL_CELL_DHORIZ }
56 };
57
58 static  int              mods(struct tbl_node *, struct tbl_cell *,
59                                 int, const char *, int *);
60 static  int              cell(struct tbl_node *, struct tbl_row *,
61                                 int, const char *, int *);
62 static  void             row(struct tbl_node *, int, const char *, int *);
63 static  struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
64                                 enum tbl_cellt, int vert);
65
66
67 static int
68 mods(struct tbl_node *tbl, struct tbl_cell *cp,
69                 int ln, const char *p, int *pos)
70 {
71         char             buf[5];
72         int              i;
73
74         /* Not all types accept modifiers. */
75
76         switch (cp->pos) {
77         case TBL_CELL_DOWN:
78                 /* FALLTHROUGH */
79         case TBL_CELL_HORIZ:
80                 /* FALLTHROUGH */
81         case TBL_CELL_DHORIZ:
82                 return(1);
83         default:
84                 break;
85         }
86
87 mod:
88         /*
89          * XXX: since, at least for now, modifiers are non-conflicting
90          * (are separable by value, regardless of position), we let
91          * modifiers come in any order.  The existing tbl doesn't let
92          * this happen.
93          */
94         switch (p[*pos]) {
95         case '\0':
96                 /* FALLTHROUGH */
97         case ' ':
98                 /* FALLTHROUGH */
99         case '\t':
100                 /* FALLTHROUGH */
101         case ',':
102                 /* FALLTHROUGH */
103         case '.':
104                 /* FALLTHROUGH */
105         case '|':
106                 return(1);
107         default:
108                 break;
109         }
110
111         /* Throw away parenthesised expression. */
112
113         if ('(' == p[*pos]) {
114                 (*pos)++;
115                 while (p[*pos] && ')' != p[*pos])
116                         (*pos)++;
117                 if (')' == p[*pos]) {
118                         (*pos)++;
119                         goto mod;
120                 }
121                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
122                     ln, *pos, NULL);
123                 return(0);
124         }
125
126         /* Parse numerical spacing from modifier string. */
127
128         if (isdigit((unsigned char)p[*pos])) {
129                 for (i = 0; i < 4; i++) {
130                         if ( ! isdigit((unsigned char)p[*pos + i]))
131                                 break;
132                         buf[i] = p[*pos + i];
133                 }
134                 buf[i] = '\0';
135
136                 /* No greater than 4 digits. */
137
138                 if (4 == i) {
139                         mandoc_msg(MANDOCERR_TBLLAYOUT,
140                             tbl->parse, ln, *pos, NULL);
141                         return(0);
142                 }
143
144                 *pos += i;
145                 cp->spacing = (size_t)atoi(buf);
146
147                 goto mod;
148                 /* NOTREACHED */
149         }
150
151         /* TODO: GNU has many more extensions. */
152
153         switch (tolower((unsigned char)p[(*pos)++])) {
154         case 'z':
155                 cp->flags |= TBL_CELL_WIGN;
156                 goto mod;
157         case 'u':
158                 cp->flags |= TBL_CELL_UP;
159                 goto mod;
160         case 'e':
161                 cp->flags |= TBL_CELL_EQUAL;
162                 goto mod;
163         case 't':
164                 cp->flags |= TBL_CELL_TALIGN;
165                 goto mod;
166         case 'd':
167                 cp->flags |= TBL_CELL_BALIGN;
168                 goto mod;
169         case 'w':  /* XXX for now, ignore minimal column width */
170                 goto mod;
171         case 'f':
172                 break;
173         case 'r':
174                 /* FALLTHROUGH */
175         case 'b':
176                 /* FALLTHROUGH */
177         case 'i':
178                 (*pos)--;
179                 break;
180         default:
181                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
182                     ln, *pos - 1, NULL);
183                 return(0);
184         }
185
186         switch (tolower((unsigned char)p[(*pos)++])) {
187         case '3':
188                 /* FALLTHROUGH */
189         case 'b':
190                 cp->flags |= TBL_CELL_BOLD;
191                 goto mod;
192         case '2':
193                 /* FALLTHROUGH */
194         case 'i':
195                 cp->flags |= TBL_CELL_ITALIC;
196                 goto mod;
197         case '1':
198                 /* FALLTHROUGH */
199         case 'r':
200                 goto mod;
201         default:
202                 break;
203         }
204
205         mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
206             ln, *pos - 1, NULL);
207         return(0);
208 }
209
210 static int
211 cell(struct tbl_node *tbl, struct tbl_row *rp,
212                 int ln, const char *p, int *pos)
213 {
214         int              vert, i;
215         enum tbl_cellt   c;
216
217         /* Handle vertical lines. */
218
219         for (vert = 0; '|' == p[*pos]; ++*pos)
220                 vert++;
221         while (' ' == p[*pos])
222                 (*pos)++;
223
224         /* Handle trailing vertical lines */
225
226         if ('.' == p[*pos] || '\0' == p[*pos]) {
227                 rp->vert = vert;
228                 return(1);
229         }
230
231         /* Parse the column position (`c', `l', `r', ...). */
232
233         for (i = 0; i < KEYS_MAX; i++)
234                 if (tolower((unsigned char)p[*pos]) == keys[i].name)
235                         break;
236
237         if (KEYS_MAX == i) {
238                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
239                     ln, *pos, NULL);
240                 return(0);
241         }
242
243         c = keys[i].key;
244
245         /*
246          * If a span cell is found first, raise a warning and abort the
247          * parse.  If a span cell is found and the last layout element
248          * isn't a "normal" layout, bail.
249          *
250          * FIXME: recover from this somehow?
251          */
252
253         if (TBL_CELL_SPAN == c) {
254                 if (NULL == rp->first) {
255                         mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
256                             ln, *pos, NULL);
257                         return(0);
258                 } else if (rp->last)
259                         switch (rp->last->pos) {
260                         case TBL_CELL_HORIZ:
261                                 /* FALLTHROUGH */
262                         case TBL_CELL_DHORIZ:
263                                 mandoc_msg(MANDOCERR_TBLLAYOUT,
264                                     tbl->parse, ln, *pos, NULL);
265                                 return(0);
266                         default:
267                                 break;
268                         }
269         }
270
271         /*
272          * If a vertical spanner is found, we may not be in the first
273          * row.
274          */
275
276         if (TBL_CELL_DOWN == c && rp == tbl->first_row) {
277                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL);
278                 return(0);
279         }
280
281         (*pos)++;
282
283         /* Disallow adjacent spacers. */
284
285         if (vert > 2) {
286                 mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL);
287                 return(0);
288         }
289
290         /* Allocate cell then parse its modifiers. */
291
292         return(mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos));
293 }
294
295 static void
296 row(struct tbl_node *tbl, int ln, const char *p, int *pos)
297 {
298         struct tbl_row  *rp;
299
300 row:    /*
301          * EBNF describing this section:
302          *
303          * row          ::= row_list [:space:]* [.]?[\n]
304          * row_list     ::= [:space:]* row_elem row_tail
305          * row_tail     ::= [:space:]*[,] row_list |
306          *                  epsilon
307          * row_elem     ::= [\t\ ]*[:alpha:]+
308          */
309
310         rp = mandoc_calloc(1, sizeof(struct tbl_row));
311         if (tbl->last_row)
312                 tbl->last_row->next = rp;
313         else
314                 tbl->first_row = rp;
315         tbl->last_row = rp;
316
317 cell:
318         while (isspace((unsigned char)p[*pos]))
319                 (*pos)++;
320
321         /* Safely exit layout context. */
322
323         if ('.' == p[*pos]) {
324                 tbl->part = TBL_PART_DATA;
325                 if (NULL == tbl->first_row)
326                         mandoc_msg(MANDOCERR_TBLNOLAYOUT,
327                             tbl->parse, ln, *pos, NULL);
328                 (*pos)++;
329                 return;
330         }
331
332         /* End (and possibly restart) a row. */
333
334         if (',' == p[*pos]) {
335                 (*pos)++;
336                 goto row;
337         } else if ('\0' == p[*pos])
338                 return;
339
340         if ( ! cell(tbl, rp, ln, p, pos))
341                 return;
342
343         goto cell;
344         /* NOTREACHED */
345 }
346
347 int
348 tbl_layout(struct tbl_node *tbl, int ln, const char *p)
349 {
350         int              pos;
351
352         pos = 0;
353         row(tbl, ln, p, &pos);
354
355         /* Always succeed. */
356         return(1);
357 }
358
359 static struct tbl_cell *
360 cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
361                 int vert)
362 {
363         struct tbl_cell *p, *pp;
364         struct tbl_head *h, *hp;
365
366         p = mandoc_calloc(1, sizeof(struct tbl_cell));
367
368         if (NULL != (pp = rp->last)) {
369                 pp->next = p;
370                 h = pp->head->next;
371         } else {
372                 rp->first = p;
373                 h = tbl->first_head;
374         }
375         rp->last = p;
376
377         p->pos = pos;
378         p->vert = vert;
379
380         /* Re-use header. */
381
382         if (h) {
383                 p->head = h;
384                 return(p);
385         }
386
387         hp = mandoc_calloc(1, sizeof(struct tbl_head));
388         hp->ident = tbl->opts.cols++;
389         hp->vert = vert;
390
391         if (tbl->last_head) {
392                 hp->prev = tbl->last_head;
393                 tbl->last_head->next = hp;
394         } else
395                 tbl->first_head = hp;
396         tbl->last_head = hp;
397
398         p->head = hp;
399         return(p);
400 }