1*0a6a1f1dSLionel Sambuc /* Id: tbl_layout.c,v 1.23 2012/05/27 17:54:54 schwarze Exp */
2d65f6f70SBen Gras /*
392395e9cSLionel Sambuc * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*0a6a1f1dSLionel Sambuc * Copyright (c) 2012 Ingo Schwarze <schwarze@openbsd.org>
5d65f6f70SBen Gras *
6d65f6f70SBen Gras * Permission to use, copy, modify, and distribute this software for any
7d65f6f70SBen Gras * purpose with or without fee is hereby granted, provided that the above
8d65f6f70SBen Gras * copyright notice and this permission notice appear in all copies.
9d65f6f70SBen Gras *
10d65f6f70SBen Gras * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d65f6f70SBen Gras * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d65f6f70SBen Gras * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d65f6f70SBen Gras * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d65f6f70SBen Gras * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d65f6f70SBen Gras * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d65f6f70SBen Gras * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d65f6f70SBen Gras */
1892395e9cSLionel Sambuc #ifdef HAVE_CONFIG_H
1992395e9cSLionel Sambuc #include "config.h"
2092395e9cSLionel Sambuc #endif
2192395e9cSLionel Sambuc
22d65f6f70SBen Gras #include <assert.h>
23d65f6f70SBen Gras #include <ctype.h>
24d65f6f70SBen Gras #include <stdlib.h>
25d65f6f70SBen Gras #include <string.h>
26d65f6f70SBen Gras #include <time.h>
27d65f6f70SBen Gras
28d65f6f70SBen Gras #include "mandoc.h"
29d65f6f70SBen Gras #include "libmandoc.h"
30d65f6f70SBen Gras #include "libroff.h"
31d65f6f70SBen Gras
32d65f6f70SBen Gras struct tbl_phrase {
33d65f6f70SBen Gras char name;
34d65f6f70SBen Gras enum tbl_cellt key;
35d65f6f70SBen Gras };
36d65f6f70SBen Gras
37d65f6f70SBen Gras /*
38d65f6f70SBen Gras * FIXME: we can make this parse a lot nicer by, when an error is
39d65f6f70SBen Gras * encountered in a layout key, bailing to the next key (i.e. to the
40d65f6f70SBen Gras * next whitespace then continuing).
41d65f6f70SBen Gras */
42d65f6f70SBen Gras
43d65f6f70SBen Gras #define KEYS_MAX 11
44d65f6f70SBen Gras
45d65f6f70SBen Gras static const struct tbl_phrase keys[KEYS_MAX] = {
46d65f6f70SBen Gras { 'c', TBL_CELL_CENTRE },
47d65f6f70SBen Gras { 'r', TBL_CELL_RIGHT },
48d65f6f70SBen Gras { 'l', TBL_CELL_LEFT },
49d65f6f70SBen Gras { 'n', TBL_CELL_NUMBER },
50d65f6f70SBen Gras { 's', TBL_CELL_SPAN },
51d65f6f70SBen Gras { 'a', TBL_CELL_LONG },
52d65f6f70SBen Gras { '^', TBL_CELL_DOWN },
53d65f6f70SBen Gras { '-', TBL_CELL_HORIZ },
54d65f6f70SBen Gras { '_', TBL_CELL_HORIZ },
55*0a6a1f1dSLionel Sambuc { '=', TBL_CELL_DHORIZ }
56d65f6f70SBen Gras };
57d65f6f70SBen Gras
58d65f6f70SBen Gras static int mods(struct tbl_node *, struct tbl_cell *,
59d65f6f70SBen Gras int, const char *, int *);
60d65f6f70SBen Gras static int cell(struct tbl_node *, struct tbl_row *,
61d65f6f70SBen Gras int, const char *, int *);
62d65f6f70SBen Gras static void row(struct tbl_node *, int, const char *, int *);
63*0a6a1f1dSLionel Sambuc static struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
64*0a6a1f1dSLionel Sambuc enum tbl_cellt, int vert);
65d65f6f70SBen Gras
66d65f6f70SBen Gras static int
mods(struct tbl_node * tbl,struct tbl_cell * cp,int ln,const char * p,int * pos)67d65f6f70SBen Gras mods(struct tbl_node *tbl, struct tbl_cell *cp,
68d65f6f70SBen Gras int ln, const char *p, int *pos)
69d65f6f70SBen Gras {
70d65f6f70SBen Gras char buf[5];
71d65f6f70SBen Gras int i;
72d65f6f70SBen Gras
7392395e9cSLionel Sambuc /* Not all types accept modifiers. */
7492395e9cSLionel Sambuc
7592395e9cSLionel Sambuc switch (cp->pos) {
7692395e9cSLionel Sambuc case (TBL_CELL_DOWN):
7792395e9cSLionel Sambuc /* FALLTHROUGH */
7892395e9cSLionel Sambuc case (TBL_CELL_HORIZ):
7992395e9cSLionel Sambuc /* FALLTHROUGH */
8092395e9cSLionel Sambuc case (TBL_CELL_DHORIZ):
8192395e9cSLionel Sambuc return(1);
8292395e9cSLionel Sambuc default:
8392395e9cSLionel Sambuc break;
8492395e9cSLionel Sambuc }
8592395e9cSLionel Sambuc
86d65f6f70SBen Gras mod:
87d65f6f70SBen Gras /*
88d65f6f70SBen Gras * XXX: since, at least for now, modifiers are non-conflicting
89d65f6f70SBen Gras * (are separable by value, regardless of position), we let
90d65f6f70SBen Gras * modifiers come in any order. The existing tbl doesn't let
91d65f6f70SBen Gras * this happen.
92d65f6f70SBen Gras */
93d65f6f70SBen Gras switch (p[*pos]) {
94d65f6f70SBen Gras case ('\0'):
95d65f6f70SBen Gras /* FALLTHROUGH */
96d65f6f70SBen Gras case (' '):
97d65f6f70SBen Gras /* FALLTHROUGH */
98d65f6f70SBen Gras case ('\t'):
99d65f6f70SBen Gras /* FALLTHROUGH */
100d65f6f70SBen Gras case (','):
101d65f6f70SBen Gras /* FALLTHROUGH */
102d65f6f70SBen Gras case ('.'):
103d65f6f70SBen Gras return(1);
104d65f6f70SBen Gras default:
105d65f6f70SBen Gras break;
106d65f6f70SBen Gras }
107d65f6f70SBen Gras
108d65f6f70SBen Gras /* Throw away parenthesised expression. */
109d65f6f70SBen Gras
110d65f6f70SBen Gras if ('(' == p[*pos]) {
111d65f6f70SBen Gras (*pos)++;
112d65f6f70SBen Gras while (p[*pos] && ')' != p[*pos])
113d65f6f70SBen Gras (*pos)++;
114d65f6f70SBen Gras if (')' == p[*pos]) {
115d65f6f70SBen Gras (*pos)++;
116d65f6f70SBen Gras goto mod;
117d65f6f70SBen Gras }
11892395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLLAYOUT,
11992395e9cSLionel Sambuc tbl->parse, ln, *pos, NULL);
120d65f6f70SBen Gras return(0);
121d65f6f70SBen Gras }
122d65f6f70SBen Gras
123d65f6f70SBen Gras /* Parse numerical spacing from modifier string. */
124d65f6f70SBen Gras
125d65f6f70SBen Gras if (isdigit((unsigned char)p[*pos])) {
126d65f6f70SBen Gras for (i = 0; i < 4; i++) {
127d65f6f70SBen Gras if ( ! isdigit((unsigned char)p[*pos + i]))
128d65f6f70SBen Gras break;
129d65f6f70SBen Gras buf[i] = p[*pos + i];
130d65f6f70SBen Gras }
131d65f6f70SBen Gras buf[i] = '\0';
132d65f6f70SBen Gras
133d65f6f70SBen Gras /* No greater than 4 digits. */
134d65f6f70SBen Gras
135d65f6f70SBen Gras if (4 == i) {
13692395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
13792395e9cSLionel Sambuc ln, *pos, NULL);
138d65f6f70SBen Gras return(0);
139d65f6f70SBen Gras }
140d65f6f70SBen Gras
141d65f6f70SBen Gras *pos += i;
14292395e9cSLionel Sambuc cp->spacing = (size_t)atoi(buf);
143d65f6f70SBen Gras
144d65f6f70SBen Gras goto mod;
145d65f6f70SBen Gras /* NOTREACHED */
146d65f6f70SBen Gras }
147d65f6f70SBen Gras
148d65f6f70SBen Gras /* TODO: GNU has many more extensions. */
149d65f6f70SBen Gras
150d65f6f70SBen Gras switch (tolower((unsigned char)p[(*pos)++])) {
151d65f6f70SBen Gras case ('z'):
152d65f6f70SBen Gras cp->flags |= TBL_CELL_WIGN;
153d65f6f70SBen Gras goto mod;
154d65f6f70SBen Gras case ('u'):
155d65f6f70SBen Gras cp->flags |= TBL_CELL_UP;
156d65f6f70SBen Gras goto mod;
157d65f6f70SBen Gras case ('e'):
158d65f6f70SBen Gras cp->flags |= TBL_CELL_EQUAL;
159d65f6f70SBen Gras goto mod;
160d65f6f70SBen Gras case ('t'):
161d65f6f70SBen Gras cp->flags |= TBL_CELL_TALIGN;
162d65f6f70SBen Gras goto mod;
163d65f6f70SBen Gras case ('d'):
164d65f6f70SBen Gras cp->flags |= TBL_CELL_BALIGN;
165d65f6f70SBen Gras goto mod;
166d65f6f70SBen Gras case ('w'): /* XXX for now, ignore minimal column width */
167d65f6f70SBen Gras goto mod;
168d65f6f70SBen Gras case ('f'):
169d65f6f70SBen Gras break;
17092395e9cSLionel Sambuc case ('r'):
17192395e9cSLionel Sambuc /* FALLTHROUGH */
172d65f6f70SBen Gras case ('b'):
173d65f6f70SBen Gras /* FALLTHROUGH */
174d65f6f70SBen Gras case ('i'):
175d65f6f70SBen Gras (*pos)--;
176d65f6f70SBen Gras break;
177d65f6f70SBen Gras default:
17892395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
17992395e9cSLionel Sambuc ln, *pos - 1, NULL);
180d65f6f70SBen Gras return(0);
181d65f6f70SBen Gras }
182d65f6f70SBen Gras
183d65f6f70SBen Gras switch (tolower((unsigned char)p[(*pos)++])) {
18492395e9cSLionel Sambuc case ('3'):
18592395e9cSLionel Sambuc /* FALLTHROUGH */
186d65f6f70SBen Gras case ('b'):
187d65f6f70SBen Gras cp->flags |= TBL_CELL_BOLD;
188d65f6f70SBen Gras goto mod;
18992395e9cSLionel Sambuc case ('2'):
19092395e9cSLionel Sambuc /* FALLTHROUGH */
191d65f6f70SBen Gras case ('i'):
192d65f6f70SBen Gras cp->flags |= TBL_CELL_ITALIC;
193d65f6f70SBen Gras goto mod;
19492395e9cSLionel Sambuc case ('1'):
19592395e9cSLionel Sambuc /* FALLTHROUGH */
19692395e9cSLionel Sambuc case ('r'):
19792395e9cSLionel Sambuc goto mod;
198d65f6f70SBen Gras default:
199d65f6f70SBen Gras break;
200d65f6f70SBen Gras }
201d65f6f70SBen Gras
20292395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
20392395e9cSLionel Sambuc ln, *pos - 1, NULL);
204d65f6f70SBen Gras return(0);
205d65f6f70SBen Gras }
206d65f6f70SBen Gras
207d65f6f70SBen Gras static int
cell(struct tbl_node * tbl,struct tbl_row * rp,int ln,const char * p,int * pos)208d65f6f70SBen Gras cell(struct tbl_node *tbl, struct tbl_row *rp,
209d65f6f70SBen Gras int ln, const char *p, int *pos)
210d65f6f70SBen Gras {
211*0a6a1f1dSLionel Sambuc int vert, i;
212d65f6f70SBen Gras enum tbl_cellt c;
213d65f6f70SBen Gras
214*0a6a1f1dSLionel Sambuc /* Handle vertical lines. */
215*0a6a1f1dSLionel Sambuc
216*0a6a1f1dSLionel Sambuc for (vert = 0; '|' == p[*pos]; ++*pos)
217*0a6a1f1dSLionel Sambuc vert++;
218*0a6a1f1dSLionel Sambuc while (' ' == p[*pos])
219*0a6a1f1dSLionel Sambuc (*pos)++;
220*0a6a1f1dSLionel Sambuc
221*0a6a1f1dSLionel Sambuc /* Parse the column position (`c', `l', `r', ...). */
222d65f6f70SBen Gras
223d65f6f70SBen Gras for (i = 0; i < KEYS_MAX; i++)
224d65f6f70SBen Gras if (tolower((unsigned char)p[*pos]) == keys[i].name)
225d65f6f70SBen Gras break;
226d65f6f70SBen Gras
227d65f6f70SBen Gras if (KEYS_MAX == i) {
22892395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
22992395e9cSLionel Sambuc ln, *pos, NULL);
230d65f6f70SBen Gras return(0);
231d65f6f70SBen Gras }
232d65f6f70SBen Gras
233d65f6f70SBen Gras c = keys[i].key;
234d65f6f70SBen Gras
235d65f6f70SBen Gras /*
236d65f6f70SBen Gras * If a span cell is found first, raise a warning and abort the
23792395e9cSLionel Sambuc * parse. If a span cell is found and the last layout element
23892395e9cSLionel Sambuc * isn't a "normal" layout, bail.
23992395e9cSLionel Sambuc *
24092395e9cSLionel Sambuc * FIXME: recover from this somehow?
241d65f6f70SBen Gras */
242d65f6f70SBen Gras
24392395e9cSLionel Sambuc if (TBL_CELL_SPAN == c) {
24492395e9cSLionel Sambuc if (NULL == rp->first) {
24592395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
24692395e9cSLionel Sambuc ln, *pos, NULL);
24792395e9cSLionel Sambuc return(0);
24892395e9cSLionel Sambuc } else if (rp->last)
24992395e9cSLionel Sambuc switch (rp->last->pos) {
25092395e9cSLionel Sambuc case (TBL_CELL_HORIZ):
25192395e9cSLionel Sambuc case (TBL_CELL_DHORIZ):
25292395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
25392395e9cSLionel Sambuc ln, *pos, NULL);
25492395e9cSLionel Sambuc return(0);
25592395e9cSLionel Sambuc default:
25692395e9cSLionel Sambuc break;
25792395e9cSLionel Sambuc }
25892395e9cSLionel Sambuc }
25992395e9cSLionel Sambuc
26092395e9cSLionel Sambuc /*
26192395e9cSLionel Sambuc * If a vertical spanner is found, we may not be in the first
26292395e9cSLionel Sambuc * row.
26392395e9cSLionel Sambuc */
26492395e9cSLionel Sambuc
26592395e9cSLionel Sambuc if (TBL_CELL_DOWN == c && rp == tbl->first_row) {
26692395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL);
267d65f6f70SBen Gras return(0);
268d65f6f70SBen Gras }
269d65f6f70SBen Gras
270d65f6f70SBen Gras (*pos)++;
271d65f6f70SBen Gras
272d65f6f70SBen Gras /* Disallow adjacent spacers. */
273d65f6f70SBen Gras
274*0a6a1f1dSLionel Sambuc if (vert > 2) {
27592395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL);
276d65f6f70SBen Gras return(0);
277d65f6f70SBen Gras }
278d65f6f70SBen Gras
279d65f6f70SBen Gras /* Allocate cell then parse its modifiers. */
280d65f6f70SBen Gras
281*0a6a1f1dSLionel Sambuc return(mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos));
282d65f6f70SBen Gras }
283d65f6f70SBen Gras
284d65f6f70SBen Gras
285d65f6f70SBen Gras static void
row(struct tbl_node * tbl,int ln,const char * p,int * pos)286d65f6f70SBen Gras row(struct tbl_node *tbl, int ln, const char *p, int *pos)
287d65f6f70SBen Gras {
288d65f6f70SBen Gras struct tbl_row *rp;
289d65f6f70SBen Gras
290d65f6f70SBen Gras row: /*
291d65f6f70SBen Gras * EBNF describing this section:
292d65f6f70SBen Gras *
293d65f6f70SBen Gras * row ::= row_list [:space:]* [.]?[\n]
294d65f6f70SBen Gras * row_list ::= [:space:]* row_elem row_tail
295d65f6f70SBen Gras * row_tail ::= [:space:]*[,] row_list |
296d65f6f70SBen Gras * epsilon
297d65f6f70SBen Gras * row_elem ::= [\t\ ]*[:alpha:]+
298d65f6f70SBen Gras */
299d65f6f70SBen Gras
300d65f6f70SBen Gras rp = mandoc_calloc(1, sizeof(struct tbl_row));
301*0a6a1f1dSLionel Sambuc if (tbl->last_row)
302d65f6f70SBen Gras tbl->last_row->next = rp;
303*0a6a1f1dSLionel Sambuc else
304*0a6a1f1dSLionel Sambuc tbl->first_row = rp;
305d65f6f70SBen Gras tbl->last_row = rp;
306d65f6f70SBen Gras
307d65f6f70SBen Gras cell:
308d65f6f70SBen Gras while (isspace((unsigned char)p[*pos]))
309d65f6f70SBen Gras (*pos)++;
310d65f6f70SBen Gras
311d65f6f70SBen Gras /* Safely exit layout context. */
312d65f6f70SBen Gras
313d65f6f70SBen Gras if ('.' == p[*pos]) {
314d65f6f70SBen Gras tbl->part = TBL_PART_DATA;
315d65f6f70SBen Gras if (NULL == tbl->first_row)
31692395e9cSLionel Sambuc mandoc_msg(MANDOCERR_TBLNOLAYOUT, tbl->parse,
31792395e9cSLionel Sambuc ln, *pos, NULL);
318d65f6f70SBen Gras (*pos)++;
319d65f6f70SBen Gras return;
320d65f6f70SBen Gras }
321d65f6f70SBen Gras
322d65f6f70SBen Gras /* End (and possibly restart) a row. */
323d65f6f70SBen Gras
324d65f6f70SBen Gras if (',' == p[*pos]) {
325d65f6f70SBen Gras (*pos)++;
326d65f6f70SBen Gras goto row;
327d65f6f70SBen Gras } else if ('\0' == p[*pos])
328d65f6f70SBen Gras return;
329d65f6f70SBen Gras
330d65f6f70SBen Gras if ( ! cell(tbl, rp, ln, p, pos))
331d65f6f70SBen Gras return;
332d65f6f70SBen Gras
333d65f6f70SBen Gras goto cell;
334d65f6f70SBen Gras /* NOTREACHED */
335d65f6f70SBen Gras }
336d65f6f70SBen Gras
337d65f6f70SBen Gras int
tbl_layout(struct tbl_node * tbl,int ln,const char * p)338d65f6f70SBen Gras tbl_layout(struct tbl_node *tbl, int ln, const char *p)
339d65f6f70SBen Gras {
340d65f6f70SBen Gras int pos;
341d65f6f70SBen Gras
342d65f6f70SBen Gras pos = 0;
343d65f6f70SBen Gras row(tbl, ln, p, &pos);
344d65f6f70SBen Gras
345d65f6f70SBen Gras /* Always succeed. */
346d65f6f70SBen Gras return(1);
347d65f6f70SBen Gras }
348d65f6f70SBen Gras
349d65f6f70SBen Gras static struct tbl_cell *
cell_alloc(struct tbl_node * tbl,struct tbl_row * rp,enum tbl_cellt pos,int vert)350*0a6a1f1dSLionel Sambuc cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
351*0a6a1f1dSLionel Sambuc int vert)
352d65f6f70SBen Gras {
353d65f6f70SBen Gras struct tbl_cell *p, *pp;
354d65f6f70SBen Gras struct tbl_head *h, *hp;
355d65f6f70SBen Gras
356d65f6f70SBen Gras p = mandoc_calloc(1, sizeof(struct tbl_cell));
357d65f6f70SBen Gras
358d65f6f70SBen Gras if (NULL != (pp = rp->last)) {
359*0a6a1f1dSLionel Sambuc pp->next = p;
360*0a6a1f1dSLionel Sambuc h = pp->head->next;
361*0a6a1f1dSLionel Sambuc } else {
362*0a6a1f1dSLionel Sambuc rp->first = p;
363*0a6a1f1dSLionel Sambuc h = tbl->first_head;
364*0a6a1f1dSLionel Sambuc }
365d65f6f70SBen Gras rp->last = p;
366d65f6f70SBen Gras
367d65f6f70SBen Gras p->pos = pos;
368*0a6a1f1dSLionel Sambuc p->vert = vert;
369d65f6f70SBen Gras
370*0a6a1f1dSLionel Sambuc /* Re-use header. */
371d65f6f70SBen Gras
372d65f6f70SBen Gras if (h) {
373d65f6f70SBen Gras p->head = h;
374d65f6f70SBen Gras return(p);
375d65f6f70SBen Gras }
376d65f6f70SBen Gras
377d65f6f70SBen Gras hp = mandoc_calloc(1, sizeof(struct tbl_head));
378d65f6f70SBen Gras hp->ident = tbl->opts.cols++;
379*0a6a1f1dSLionel Sambuc hp->vert = vert;
380d65f6f70SBen Gras
381d65f6f70SBen Gras if (tbl->last_head) {
382d65f6f70SBen Gras hp->prev = tbl->last_head;
383d65f6f70SBen Gras tbl->last_head->next = hp;
384d65f6f70SBen Gras } else
385*0a6a1f1dSLionel Sambuc tbl->first_head = hp;
386*0a6a1f1dSLionel Sambuc tbl->last_head = hp;
387d65f6f70SBen Gras
388d65f6f70SBen Gras p->head = hp;
389d65f6f70SBen Gras return(p);
390d65f6f70SBen Gras }
391