1*260e9a87SYuri Pankov /* $Id: tbl_layout.c,v 1.38 2015/02/10 11:03:13 schwarze Exp $ */
295c635efSGarrett D'Amore /*
395c635efSGarrett D'Amore * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*260e9a87SYuri Pankov * Copyright (c) 2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore *
695c635efSGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore *
1095c635efSGarrett D'Amore * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395c635efSGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore */
1895c635efSGarrett D'Amore #include "config.h"
1995c635efSGarrett D'Amore
20*260e9a87SYuri Pankov #include <sys/types.h>
21*260e9a87SYuri Pankov
2295c635efSGarrett D'Amore #include <ctype.h>
2395c635efSGarrett D'Amore #include <stdlib.h>
2495c635efSGarrett D'Amore #include <string.h>
2595c635efSGarrett D'Amore #include <time.h>
2695c635efSGarrett D'Amore
2795c635efSGarrett D'Amore #include "mandoc.h"
28*260e9a87SYuri Pankov #include "mandoc_aux.h"
2995c635efSGarrett D'Amore #include "libmandoc.h"
3095c635efSGarrett D'Amore #include "libroff.h"
3195c635efSGarrett D'Amore
3295c635efSGarrett D'Amore struct tbl_phrase {
3395c635efSGarrett D'Amore char name;
3495c635efSGarrett D'Amore enum tbl_cellt key;
3595c635efSGarrett D'Amore };
3695c635efSGarrett D'Amore
37*260e9a87SYuri Pankov static const struct tbl_phrase keys[] = {
3895c635efSGarrett D'Amore { 'c', TBL_CELL_CENTRE },
3995c635efSGarrett D'Amore { 'r', TBL_CELL_RIGHT },
4095c635efSGarrett D'Amore { 'l', TBL_CELL_LEFT },
4195c635efSGarrett D'Amore { 'n', TBL_CELL_NUMBER },
4295c635efSGarrett D'Amore { 's', TBL_CELL_SPAN },
4395c635efSGarrett D'Amore { 'a', TBL_CELL_LONG },
4495c635efSGarrett D'Amore { '^', TBL_CELL_DOWN },
4595c635efSGarrett D'Amore { '-', TBL_CELL_HORIZ },
4695c635efSGarrett D'Amore { '_', TBL_CELL_HORIZ },
47698f87a4SGarrett D'Amore { '=', TBL_CELL_DHORIZ }
4895c635efSGarrett D'Amore };
4995c635efSGarrett D'Amore
50*260e9a87SYuri Pankov #define KEYS_MAX ((int)(sizeof(keys)/sizeof(keys[0])))
5195c635efSGarrett D'Amore
52*260e9a87SYuri Pankov static void mods(struct tbl_node *, struct tbl_cell *,
53*260e9a87SYuri Pankov int, const char *, int *);
54*260e9a87SYuri Pankov static void cell(struct tbl_node *, struct tbl_row *,
55*260e9a87SYuri Pankov int, const char *, int *);
56*260e9a87SYuri Pankov static struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
57*260e9a87SYuri Pankov enum tbl_cellt);
58*260e9a87SYuri Pankov
59*260e9a87SYuri Pankov
60*260e9a87SYuri Pankov static void
mods(struct tbl_node * tbl,struct tbl_cell * cp,int ln,const char * p,int * pos)6195c635efSGarrett D'Amore mods(struct tbl_node *tbl, struct tbl_cell *cp,
6295c635efSGarrett D'Amore int ln, const char *p, int *pos)
6395c635efSGarrett D'Amore {
64*260e9a87SYuri Pankov char *endptr;
6595c635efSGarrett D'Amore
6695c635efSGarrett D'Amore mod:
67*260e9a87SYuri Pankov while (p[*pos] == ' ' || p[*pos] == '\t')
68*260e9a87SYuri Pankov (*pos)++;
69*260e9a87SYuri Pankov
70*260e9a87SYuri Pankov /* Row delimiters and cell specifiers end modifier lists. */
71*260e9a87SYuri Pankov
72*260e9a87SYuri Pankov if (strchr(".,-=^_ACLNRSaclnrs", p[*pos]) != NULL)
73*260e9a87SYuri Pankov return;
7495c635efSGarrett D'Amore
7595c635efSGarrett D'Amore /* Throw away parenthesised expression. */
7695c635efSGarrett D'Amore
7795c635efSGarrett D'Amore if ('(' == p[*pos]) {
7895c635efSGarrett D'Amore (*pos)++;
7995c635efSGarrett D'Amore while (p[*pos] && ')' != p[*pos])
8095c635efSGarrett D'Amore (*pos)++;
8195c635efSGarrett D'Amore if (')' == p[*pos]) {
8295c635efSGarrett D'Amore (*pos)++;
8395c635efSGarrett D'Amore goto mod;
8495c635efSGarrett D'Amore }
85*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, tbl->parse,
86*260e9a87SYuri Pankov ln, *pos, NULL);
87*260e9a87SYuri Pankov return;
8895c635efSGarrett D'Amore }
8995c635efSGarrett D'Amore
9095c635efSGarrett D'Amore /* Parse numerical spacing from modifier string. */
9195c635efSGarrett D'Amore
9295c635efSGarrett D'Amore if (isdigit((unsigned char)p[*pos])) {
93*260e9a87SYuri Pankov cp->spacing = strtoull(p + *pos, &endptr, 10);
94*260e9a87SYuri Pankov *pos = endptr - p;
9595c635efSGarrett D'Amore goto mod;
9695c635efSGarrett D'Amore }
9795c635efSGarrett D'Amore
9895c635efSGarrett D'Amore switch (tolower((unsigned char)p[(*pos)++])) {
99*260e9a87SYuri Pankov case 'b':
10095c635efSGarrett D'Amore cp->flags |= TBL_CELL_BOLD;
10195c635efSGarrett D'Amore goto mod;
102*260e9a87SYuri Pankov case 'd':
103*260e9a87SYuri Pankov cp->flags |= TBL_CELL_BALIGN;
104*260e9a87SYuri Pankov goto mod;
105*260e9a87SYuri Pankov case 'e':
106*260e9a87SYuri Pankov cp->flags |= TBL_CELL_EQUAL;
107*260e9a87SYuri Pankov goto mod;
108*260e9a87SYuri Pankov case 'f':
109*260e9a87SYuri Pankov break;
110*260e9a87SYuri Pankov case 'i':
11195c635efSGarrett D'Amore cp->flags |= TBL_CELL_ITALIC;
11295c635efSGarrett D'Amore goto mod;
113*260e9a87SYuri Pankov case 'm':
114*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, tbl->parse,
115*260e9a87SYuri Pankov ln, *pos, "m");
116*260e9a87SYuri Pankov goto mod;
117*260e9a87SYuri Pankov case 'p':
11895c635efSGarrett D'Amore /* FALLTHROUGH */
119*260e9a87SYuri Pankov case 'v':
120*260e9a87SYuri Pankov if (p[*pos] == '-' || p[*pos] == '+')
121*260e9a87SYuri Pankov (*pos)++;
122*260e9a87SYuri Pankov while (isdigit((unsigned char)p[*pos]))
123*260e9a87SYuri Pankov (*pos)++;
124*260e9a87SYuri Pankov goto mod;
125*260e9a87SYuri Pankov case 't':
126*260e9a87SYuri Pankov cp->flags |= TBL_CELL_TALIGN;
127*260e9a87SYuri Pankov goto mod;
128*260e9a87SYuri Pankov case 'u':
129*260e9a87SYuri Pankov cp->flags |= TBL_CELL_UP;
130*260e9a87SYuri Pankov goto mod;
131*260e9a87SYuri Pankov case 'w': /* XXX for now, ignore minimal column width */
132*260e9a87SYuri Pankov goto mod;
133*260e9a87SYuri Pankov case 'x':
134*260e9a87SYuri Pankov cp->flags |= TBL_CELL_WMAX;
135*260e9a87SYuri Pankov goto mod;
136*260e9a87SYuri Pankov case 'z':
137*260e9a87SYuri Pankov cp->flags |= TBL_CELL_WIGN;
138*260e9a87SYuri Pankov goto mod;
139*260e9a87SYuri Pankov case '|':
140*260e9a87SYuri Pankov if (cp->vert < 2)
141*260e9a87SYuri Pankov cp->vert++;
142*260e9a87SYuri Pankov else
143*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
144*260e9a87SYuri Pankov tbl->parse, ln, *pos - 1, NULL);
14595c635efSGarrett D'Amore goto mod;
14695c635efSGarrett D'Amore default:
147*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
148*260e9a87SYuri Pankov ln, *pos - 1, "%c", p[*pos - 1]);
149*260e9a87SYuri Pankov goto mod;
15095c635efSGarrett D'Amore }
15195c635efSGarrett D'Amore
152*260e9a87SYuri Pankov /* Ignore parenthised font names for now. */
153*260e9a87SYuri Pankov
154*260e9a87SYuri Pankov if (p[*pos] == '(')
155*260e9a87SYuri Pankov goto mod;
156*260e9a87SYuri Pankov
157*260e9a87SYuri Pankov /* Support only one-character font-names for now. */
158*260e9a87SYuri Pankov
159*260e9a87SYuri Pankov if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) {
160*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
161*260e9a87SYuri Pankov ln, *pos, "TS %s", p + *pos - 1);
162*260e9a87SYuri Pankov if (p[*pos] != '\0')
163*260e9a87SYuri Pankov (*pos)++;
164*260e9a87SYuri Pankov if (p[*pos] != '\0')
165*260e9a87SYuri Pankov (*pos)++;
166*260e9a87SYuri Pankov goto mod;
16795c635efSGarrett D'Amore }
16895c635efSGarrett D'Amore
169*260e9a87SYuri Pankov switch (p[(*pos)++]) {
170*260e9a87SYuri Pankov case '3':
171*260e9a87SYuri Pankov /* FALLTHROUGH */
172*260e9a87SYuri Pankov case 'B':
173*260e9a87SYuri Pankov cp->flags |= TBL_CELL_BOLD;
174*260e9a87SYuri Pankov goto mod;
175*260e9a87SYuri Pankov case '2':
176*260e9a87SYuri Pankov /* FALLTHROUGH */
177*260e9a87SYuri Pankov case 'I':
178*260e9a87SYuri Pankov cp->flags |= TBL_CELL_ITALIC;
179*260e9a87SYuri Pankov goto mod;
180*260e9a87SYuri Pankov case '1':
181*260e9a87SYuri Pankov /* FALLTHROUGH */
182*260e9a87SYuri Pankov case 'R':
183*260e9a87SYuri Pankov goto mod;
184*260e9a87SYuri Pankov default:
185*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
186*260e9a87SYuri Pankov ln, *pos - 1, "TS f%c", p[*pos - 1]);
187*260e9a87SYuri Pankov goto mod;
188*260e9a87SYuri Pankov }
189*260e9a87SYuri Pankov }
190*260e9a87SYuri Pankov
191*260e9a87SYuri Pankov static void
cell(struct tbl_node * tbl,struct tbl_row * rp,int ln,const char * p,int * pos)19295c635efSGarrett D'Amore cell(struct tbl_node *tbl, struct tbl_row *rp,
19395c635efSGarrett D'Amore int ln, const char *p, int *pos)
19495c635efSGarrett D'Amore {
195*260e9a87SYuri Pankov int i;
19695c635efSGarrett D'Amore enum tbl_cellt c;
19795c635efSGarrett D'Amore
198*260e9a87SYuri Pankov /* Handle leading vertical lines */
199698f87a4SGarrett D'Amore
200*260e9a87SYuri Pankov while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') {
201*260e9a87SYuri Pankov if (p[*pos] == '|') {
202*260e9a87SYuri Pankov if (rp->vert < 2)
203*260e9a87SYuri Pankov rp->vert++;
204*260e9a87SYuri Pankov else
205*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
206*260e9a87SYuri Pankov tbl->parse, ln, *pos, NULL);
207*260e9a87SYuri Pankov }
208698f87a4SGarrett D'Amore (*pos)++;
209*260e9a87SYuri Pankov }
210*260e9a87SYuri Pankov
211*260e9a87SYuri Pankov again:
212*260e9a87SYuri Pankov while (p[*pos] == ' ' || p[*pos] == '\t')
213*260e9a87SYuri Pankov (*pos)++;
214*260e9a87SYuri Pankov
215*260e9a87SYuri Pankov if (p[*pos] == '.' || p[*pos] == '\0')
216*260e9a87SYuri Pankov return;
217698f87a4SGarrett D'Amore
218698f87a4SGarrett D'Amore /* Parse the column position (`c', `l', `r', ...). */
21995c635efSGarrett D'Amore
22095c635efSGarrett D'Amore for (i = 0; i < KEYS_MAX; i++)
22195c635efSGarrett D'Amore if (tolower((unsigned char)p[*pos]) == keys[i].name)
22295c635efSGarrett D'Amore break;
22395c635efSGarrett D'Amore
224*260e9a87SYuri Pankov if (i == KEYS_MAX) {
225*260e9a87SYuri Pankov mandoc_vmsg(MANDOCERR_TBLLAYOUT_CHAR, tbl->parse,
226*260e9a87SYuri Pankov ln, *pos, "%c", p[*pos]);
227*260e9a87SYuri Pankov (*pos)++;
228*260e9a87SYuri Pankov goto again;
22995c635efSGarrett D'Amore }
23095c635efSGarrett D'Amore c = keys[i].key;
23195c635efSGarrett D'Amore
232*260e9a87SYuri Pankov /* Special cases of spanners. */
23395c635efSGarrett D'Amore
234*260e9a87SYuri Pankov if (c == TBL_CELL_SPAN) {
235*260e9a87SYuri Pankov if (rp->last == NULL)
236*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN,
237*260e9a87SYuri Pankov tbl->parse, ln, *pos, NULL);
238*260e9a87SYuri Pankov else if (rp->last->pos == TBL_CELL_HORIZ ||
239*260e9a87SYuri Pankov rp->last->pos == TBL_CELL_DHORIZ)
240*260e9a87SYuri Pankov c = rp->last->pos;
241*260e9a87SYuri Pankov } else if (c == TBL_CELL_DOWN && rp == tbl->first_row)
242*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN,
243*260e9a87SYuri Pankov tbl->parse, ln, *pos, NULL);
24495c635efSGarrett D'Amore
24595c635efSGarrett D'Amore (*pos)++;
24695c635efSGarrett D'Amore
24795c635efSGarrett D'Amore /* Allocate cell then parse its modifiers. */
24895c635efSGarrett D'Amore
249*260e9a87SYuri Pankov mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos);
25095c635efSGarrett D'Amore }
25195c635efSGarrett D'Amore
252*260e9a87SYuri Pankov void
tbl_layout(struct tbl_node * tbl,int ln,const char * p,int pos)253*260e9a87SYuri Pankov tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos)
25495c635efSGarrett D'Amore {
25595c635efSGarrett D'Amore struct tbl_row *rp;
25695c635efSGarrett D'Amore
257*260e9a87SYuri Pankov rp = NULL;
258*260e9a87SYuri Pankov for (;;) {
259*260e9a87SYuri Pankov /* Skip whitespace before and after each cell. */
260*260e9a87SYuri Pankov
261*260e9a87SYuri Pankov while (p[pos] == ' ' || p[pos] == '\t')
262*260e9a87SYuri Pankov pos++;
263*260e9a87SYuri Pankov
264*260e9a87SYuri Pankov switch (p[pos]) {
265*260e9a87SYuri Pankov case ',': /* Next row on this input line. */
266*260e9a87SYuri Pankov pos++;
267*260e9a87SYuri Pankov rp = NULL;
268*260e9a87SYuri Pankov continue;
269*260e9a87SYuri Pankov case '\0': /* Next row on next input line. */
270*260e9a87SYuri Pankov return;
271*260e9a87SYuri Pankov case '.': /* End of layout. */
272*260e9a87SYuri Pankov pos++;
273*260e9a87SYuri Pankov tbl->part = TBL_PART_DATA;
274*260e9a87SYuri Pankov
275*260e9a87SYuri Pankov /*
276*260e9a87SYuri Pankov * When the layout is completely empty,
277*260e9a87SYuri Pankov * default to one left-justified column.
27895c635efSGarrett D'Amore */
27995c635efSGarrett D'Amore
280*260e9a87SYuri Pankov if (tbl->first_row == NULL) {
281*260e9a87SYuri Pankov tbl->first_row = tbl->last_row =
282*260e9a87SYuri Pankov mandoc_calloc(1, sizeof(*rp));
283*260e9a87SYuri Pankov }
284*260e9a87SYuri Pankov if (tbl->first_row->first == NULL) {
285*260e9a87SYuri Pankov mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
286*260e9a87SYuri Pankov tbl->parse, ln, pos, NULL);
287*260e9a87SYuri Pankov cell_alloc(tbl, tbl->first_row,
288*260e9a87SYuri Pankov TBL_CELL_LEFT);
289*260e9a87SYuri Pankov return;
290*260e9a87SYuri Pankov }
291*260e9a87SYuri Pankov
292*260e9a87SYuri Pankov /*
293*260e9a87SYuri Pankov * Search for the widest line
294*260e9a87SYuri Pankov * along the left and right margins.
295*260e9a87SYuri Pankov */
296*260e9a87SYuri Pankov
297*260e9a87SYuri Pankov for (rp = tbl->first_row; rp; rp = rp->next) {
298*260e9a87SYuri Pankov if (tbl->opts.lvert < rp->vert)
299*260e9a87SYuri Pankov tbl->opts.lvert = rp->vert;
300*260e9a87SYuri Pankov if (rp->last != NULL &&
301*260e9a87SYuri Pankov rp->last->col + 1 == tbl->opts.cols &&
302*260e9a87SYuri Pankov tbl->opts.rvert < rp->last->vert)
303*260e9a87SYuri Pankov tbl->opts.rvert = rp->last->vert;
304*260e9a87SYuri Pankov
305*260e9a87SYuri Pankov /* If the last line is empty, drop it. */
306*260e9a87SYuri Pankov
307*260e9a87SYuri Pankov if (rp->next != NULL &&
308*260e9a87SYuri Pankov rp->next->first == NULL) {
309*260e9a87SYuri Pankov free(rp->next);
310*260e9a87SYuri Pankov rp->next = NULL;
311*260e9a87SYuri Pankov }
312*260e9a87SYuri Pankov }
313*260e9a87SYuri Pankov return;
314*260e9a87SYuri Pankov default: /* Cell. */
315*260e9a87SYuri Pankov break;
316*260e9a87SYuri Pankov }
317*260e9a87SYuri Pankov
318*260e9a87SYuri Pankov /*
319*260e9a87SYuri Pankov * If the last line had at least one cell,
320*260e9a87SYuri Pankov * start a new one; otherwise, continue it.
321*260e9a87SYuri Pankov */
322*260e9a87SYuri Pankov
323*260e9a87SYuri Pankov if (rp == NULL) {
324*260e9a87SYuri Pankov if (tbl->last_row == NULL ||
325*260e9a87SYuri Pankov tbl->last_row->first != NULL) {
326*260e9a87SYuri Pankov rp = mandoc_calloc(1, sizeof(*rp));
327698f87a4SGarrett D'Amore if (tbl->last_row)
32895c635efSGarrett D'Amore tbl->last_row->next = rp;
329698f87a4SGarrett D'Amore else
330698f87a4SGarrett D'Amore tbl->first_row = rp;
33195c635efSGarrett D'Amore tbl->last_row = rp;
332*260e9a87SYuri Pankov } else
333*260e9a87SYuri Pankov rp = tbl->last_row;
33495c635efSGarrett D'Amore }
335*260e9a87SYuri Pankov cell(tbl, rp, ln, p, &pos);
33695c635efSGarrett D'Amore }
33795c635efSGarrett D'Amore }
33895c635efSGarrett D'Amore
33995c635efSGarrett D'Amore static struct tbl_cell *
cell_alloc(struct tbl_node * tbl,struct tbl_row * rp,enum tbl_cellt pos)340*260e9a87SYuri Pankov cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
34195c635efSGarrett D'Amore {
34295c635efSGarrett D'Amore struct tbl_cell *p, *pp;
34395c635efSGarrett D'Amore
344*260e9a87SYuri Pankov p = mandoc_calloc(1, sizeof(*p));
345*260e9a87SYuri Pankov p->pos = pos;
34695c635efSGarrett D'Amore
347*260e9a87SYuri Pankov if ((pp = rp->last) != NULL) {
348698f87a4SGarrett D'Amore pp->next = p;
349*260e9a87SYuri Pankov p->col = pp->col + 1;
350*260e9a87SYuri Pankov } else
351698f87a4SGarrett D'Amore rp->first = p;
35295c635efSGarrett D'Amore rp->last = p;
35395c635efSGarrett D'Amore
354*260e9a87SYuri Pankov if (tbl->opts.cols <= p->col)
355*260e9a87SYuri Pankov tbl->opts.cols = p->col + 1;
35695c635efSGarrett D'Amore
35795c635efSGarrett D'Amore return(p);
35895c635efSGarrett D'Amore }
359