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