xref: /freebsd/contrib/mandoc/tbl_layout.c (revision 6d38604fc532a3fc060788e3ce40464b46047eaf)
1*6d38604fSBaptiste Daroussin /*	$Id: tbl_layout.c,v 1.50 2021/08/10 12:55:04 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*6d38604fSBaptiste Daroussin  * Copyright (c) 2012, 2014, 2015, 2017, 2020, 2021
5*6d38604fSBaptiste Daroussin  *               Ingo Schwarze <schwarze@openbsd.org>
661d06d6bSBaptiste Daroussin  *
761d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
861d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
961d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
1061d06d6bSBaptiste Daroussin  *
1161d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1261d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1361d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1461d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1561d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1661d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1761d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1861d06d6bSBaptiste Daroussin  */
1961d06d6bSBaptiste Daroussin #include "config.h"
2061d06d6bSBaptiste Daroussin 
2161d06d6bSBaptiste Daroussin #include <sys/types.h>
2261d06d6bSBaptiste Daroussin 
2361d06d6bSBaptiste Daroussin #include <ctype.h>
2461d06d6bSBaptiste Daroussin #include <stdint.h>
257295610fSBaptiste Daroussin #include <stdio.h>
2661d06d6bSBaptiste Daroussin #include <stdlib.h>
2761d06d6bSBaptiste Daroussin #include <string.h>
2861d06d6bSBaptiste Daroussin #include <time.h>
2961d06d6bSBaptiste Daroussin 
3061d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
317295610fSBaptiste Daroussin #include "mandoc.h"
327295610fSBaptiste Daroussin #include "tbl.h"
3361d06d6bSBaptiste Daroussin #include "libmandoc.h"
347295610fSBaptiste Daroussin #include "tbl_int.h"
3561d06d6bSBaptiste Daroussin 
3661d06d6bSBaptiste Daroussin struct	tbl_phrase {
3761d06d6bSBaptiste Daroussin 	char		 name;
3861d06d6bSBaptiste Daroussin 	enum tbl_cellt	 key;
3961d06d6bSBaptiste Daroussin };
4061d06d6bSBaptiste Daroussin 
4161d06d6bSBaptiste Daroussin static	const struct tbl_phrase keys[] = {
4261d06d6bSBaptiste Daroussin 	{ 'c',		 TBL_CELL_CENTRE },
4361d06d6bSBaptiste Daroussin 	{ 'r',		 TBL_CELL_RIGHT },
4461d06d6bSBaptiste Daroussin 	{ 'l',		 TBL_CELL_LEFT },
4561d06d6bSBaptiste Daroussin 	{ 'n',		 TBL_CELL_NUMBER },
4661d06d6bSBaptiste Daroussin 	{ 's',		 TBL_CELL_SPAN },
4761d06d6bSBaptiste Daroussin 	{ 'a',		 TBL_CELL_LONG },
4861d06d6bSBaptiste Daroussin 	{ '^',		 TBL_CELL_DOWN },
4961d06d6bSBaptiste Daroussin 	{ '-',		 TBL_CELL_HORIZ },
5061d06d6bSBaptiste Daroussin 	{ '_',		 TBL_CELL_HORIZ },
5161d06d6bSBaptiste Daroussin 	{ '=',		 TBL_CELL_DHORIZ }
5261d06d6bSBaptiste Daroussin };
5361d06d6bSBaptiste Daroussin 
5461d06d6bSBaptiste Daroussin #define KEYS_MAX ((int)(sizeof(keys)/sizeof(keys[0])))
5561d06d6bSBaptiste Daroussin 
5661d06d6bSBaptiste Daroussin static	void		 mods(struct tbl_node *, struct tbl_cell *,
5761d06d6bSBaptiste Daroussin 				int, const char *, int *);
5861d06d6bSBaptiste Daroussin static	void		 cell(struct tbl_node *, struct tbl_row *,
5961d06d6bSBaptiste Daroussin 				int, const char *, int *);
6061d06d6bSBaptiste Daroussin static	struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
6161d06d6bSBaptiste Daroussin 				enum tbl_cellt);
6261d06d6bSBaptiste Daroussin 
6361d06d6bSBaptiste Daroussin 
6461d06d6bSBaptiste Daroussin static void
mods(struct tbl_node * tbl,struct tbl_cell * cp,int ln,const char * p,int * pos)6561d06d6bSBaptiste Daroussin mods(struct tbl_node *tbl, struct tbl_cell *cp,
6661d06d6bSBaptiste Daroussin 		int ln, const char *p, int *pos)
6761d06d6bSBaptiste Daroussin {
6861d06d6bSBaptiste Daroussin 	char		*endptr;
69*6d38604fSBaptiste Daroussin 	unsigned long	 spacing;
7061d06d6bSBaptiste Daroussin 	size_t		 sz;
71*6d38604fSBaptiste Daroussin 	int		 isz;
72*6d38604fSBaptiste Daroussin 	enum mandoc_esc	 fontesc;
7361d06d6bSBaptiste Daroussin 
7461d06d6bSBaptiste Daroussin mod:
7561d06d6bSBaptiste Daroussin 	while (p[*pos] == ' ' || p[*pos] == '\t')
7661d06d6bSBaptiste Daroussin 		(*pos)++;
7761d06d6bSBaptiste Daroussin 
7861d06d6bSBaptiste Daroussin 	/* Row delimiters and cell specifiers end modifier lists. */
7961d06d6bSBaptiste Daroussin 
8061d06d6bSBaptiste Daroussin 	if (strchr(".,-=^_ACLNRSaclnrs", p[*pos]) != NULL)
8161d06d6bSBaptiste Daroussin 		return;
8261d06d6bSBaptiste Daroussin 
8361d06d6bSBaptiste Daroussin 	/* Throw away parenthesised expression. */
8461d06d6bSBaptiste Daroussin 
8561d06d6bSBaptiste Daroussin 	if ('(' == p[*pos]) {
8661d06d6bSBaptiste Daroussin 		(*pos)++;
8761d06d6bSBaptiste Daroussin 		while (p[*pos] && ')' != p[*pos])
8861d06d6bSBaptiste Daroussin 			(*pos)++;
8961d06d6bSBaptiste Daroussin 		if (')' == p[*pos]) {
9061d06d6bSBaptiste Daroussin 			(*pos)++;
9161d06d6bSBaptiste Daroussin 			goto mod;
9261d06d6bSBaptiste Daroussin 		}
937295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, ln, *pos, NULL);
9461d06d6bSBaptiste Daroussin 		return;
9561d06d6bSBaptiste Daroussin 	}
9661d06d6bSBaptiste Daroussin 
9761d06d6bSBaptiste Daroussin 	/* Parse numerical spacing from modifier string. */
9861d06d6bSBaptiste Daroussin 
9961d06d6bSBaptiste Daroussin 	if (isdigit((unsigned char)p[*pos])) {
100*6d38604fSBaptiste Daroussin 		if ((spacing = strtoul(p + *pos, &endptr, 10)) > 9)
101*6d38604fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_TBLLAYOUT_SPC, ln, *pos,
102*6d38604fSBaptiste Daroussin 			    "%lu", spacing);
103*6d38604fSBaptiste Daroussin 		else
104*6d38604fSBaptiste Daroussin 			cp->spacing = spacing;
10561d06d6bSBaptiste Daroussin 		*pos = endptr - p;
10661d06d6bSBaptiste Daroussin 		goto mod;
10761d06d6bSBaptiste Daroussin 	}
10861d06d6bSBaptiste Daroussin 
10961d06d6bSBaptiste Daroussin 	switch (tolower((unsigned char)p[(*pos)++])) {
11061d06d6bSBaptiste Daroussin 	case 'b':
111*6d38604fSBaptiste Daroussin 		cp->font = ESCAPE_FONTBOLD;
11261d06d6bSBaptiste Daroussin 		goto mod;
11361d06d6bSBaptiste Daroussin 	case 'd':
11461d06d6bSBaptiste Daroussin 		cp->flags |= TBL_CELL_BALIGN;
11561d06d6bSBaptiste Daroussin 		goto mod;
11661d06d6bSBaptiste Daroussin 	case 'e':
11761d06d6bSBaptiste Daroussin 		cp->flags |= TBL_CELL_EQUAL;
11861d06d6bSBaptiste Daroussin 		goto mod;
11961d06d6bSBaptiste Daroussin 	case 'f':
12061d06d6bSBaptiste Daroussin 		break;
12161d06d6bSBaptiste Daroussin 	case 'i':
122*6d38604fSBaptiste Daroussin 		cp->font = ESCAPE_FONTITALIC;
12361d06d6bSBaptiste Daroussin 		goto mod;
12461d06d6bSBaptiste Daroussin 	case 'm':
1257295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, ln, *pos, "m");
12661d06d6bSBaptiste Daroussin 		goto mod;
12761d06d6bSBaptiste Daroussin 	case 'p':
12861d06d6bSBaptiste Daroussin 	case 'v':
12961d06d6bSBaptiste Daroussin 		if (p[*pos] == '-' || p[*pos] == '+')
13061d06d6bSBaptiste Daroussin 			(*pos)++;
13161d06d6bSBaptiste Daroussin 		while (isdigit((unsigned char)p[*pos]))
13261d06d6bSBaptiste Daroussin 			(*pos)++;
13361d06d6bSBaptiste Daroussin 		goto mod;
13461d06d6bSBaptiste Daroussin 	case 't':
13561d06d6bSBaptiste Daroussin 		cp->flags |= TBL_CELL_TALIGN;
13661d06d6bSBaptiste Daroussin 		goto mod;
13761d06d6bSBaptiste Daroussin 	case 'u':
13861d06d6bSBaptiste Daroussin 		cp->flags |= TBL_CELL_UP;
13961d06d6bSBaptiste Daroussin 		goto mod;
14061d06d6bSBaptiste Daroussin 	case 'w':
14161d06d6bSBaptiste Daroussin 		sz = 0;
14261d06d6bSBaptiste Daroussin 		if (p[*pos] == '(') {
14361d06d6bSBaptiste Daroussin 			(*pos)++;
14461d06d6bSBaptiste Daroussin 			while (p[*pos + sz] != '\0' && p[*pos + sz] != ')')
14561d06d6bSBaptiste Daroussin 				sz++;
14661d06d6bSBaptiste Daroussin 		} else
14761d06d6bSBaptiste Daroussin 			while (isdigit((unsigned char)p[*pos + sz]))
14861d06d6bSBaptiste Daroussin 				sz++;
14961d06d6bSBaptiste Daroussin 		if (sz) {
15061d06d6bSBaptiste Daroussin 			free(cp->wstr);
15161d06d6bSBaptiste Daroussin 			cp->wstr = mandoc_strndup(p + *pos, sz);
15261d06d6bSBaptiste Daroussin 			*pos += sz;
15361d06d6bSBaptiste Daroussin 			if (p[*pos] == ')')
15461d06d6bSBaptiste Daroussin 				(*pos)++;
15561d06d6bSBaptiste Daroussin 		}
15661d06d6bSBaptiste Daroussin 		goto mod;
15761d06d6bSBaptiste Daroussin 	case 'x':
15861d06d6bSBaptiste Daroussin 		cp->flags |= TBL_CELL_WMAX;
15961d06d6bSBaptiste Daroussin 		goto mod;
16061d06d6bSBaptiste Daroussin 	case 'z':
16161d06d6bSBaptiste Daroussin 		cp->flags |= TBL_CELL_WIGN;
16261d06d6bSBaptiste Daroussin 		goto mod;
16361d06d6bSBaptiste Daroussin 	case '|':
16461d06d6bSBaptiste Daroussin 		if (cp->vert < 2)
16561d06d6bSBaptiste Daroussin 			cp->vert++;
16661d06d6bSBaptiste Daroussin 		else
16761d06d6bSBaptiste Daroussin 			mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
1687295610fSBaptiste Daroussin 			    ln, *pos - 1, NULL);
16961d06d6bSBaptiste Daroussin 		goto mod;
17061d06d6bSBaptiste Daroussin 	default:
1717295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TBLLAYOUT_CHAR,
17261d06d6bSBaptiste Daroussin 		    ln, *pos - 1, "%c", p[*pos - 1]);
17361d06d6bSBaptiste Daroussin 		goto mod;
17461d06d6bSBaptiste Daroussin 	}
17561d06d6bSBaptiste Daroussin 
176*6d38604fSBaptiste Daroussin 	while (p[*pos] == ' ' || p[*pos] == '\t')
177*6d38604fSBaptiste Daroussin 		(*pos)++;
178*6d38604fSBaptiste Daroussin 
17961d06d6bSBaptiste Daroussin 	/* Ignore parenthised font names for now. */
18061d06d6bSBaptiste Daroussin 
18161d06d6bSBaptiste Daroussin 	if (p[*pos] == '(')
18261d06d6bSBaptiste Daroussin 		goto mod;
18361d06d6bSBaptiste Daroussin 
184*6d38604fSBaptiste Daroussin 	isz = 0;
185*6d38604fSBaptiste Daroussin 	if (p[*pos] != '\0')
186*6d38604fSBaptiste Daroussin 		isz++;
187*6d38604fSBaptiste Daroussin 	if (strchr(" \t.", p[*pos + isz]) == NULL)
188*6d38604fSBaptiste Daroussin 		isz++;
18961d06d6bSBaptiste Daroussin 
190*6d38604fSBaptiste Daroussin 	fontesc = mandoc_font(p + *pos, isz);
191*6d38604fSBaptiste Daroussin 
192*6d38604fSBaptiste Daroussin 	switch (fontesc) {
193*6d38604fSBaptiste Daroussin 	case ESCAPE_FONTPREV:
194*6d38604fSBaptiste Daroussin 	case ESCAPE_ERROR:
1957295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_FT_BAD,
19661d06d6bSBaptiste Daroussin 		    ln, *pos, "TS %s", p + *pos - 1);
197*6d38604fSBaptiste Daroussin 		break;
19861d06d6bSBaptiste Daroussin 	default:
199*6d38604fSBaptiste Daroussin 		cp->font = fontesc;
200*6d38604fSBaptiste Daroussin 		break;
20161d06d6bSBaptiste Daroussin 	}
202*6d38604fSBaptiste Daroussin 	*pos += isz;
203*6d38604fSBaptiste Daroussin 	goto mod;
20461d06d6bSBaptiste Daroussin }
20561d06d6bSBaptiste Daroussin 
20661d06d6bSBaptiste Daroussin static void
cell(struct tbl_node * tbl,struct tbl_row * rp,int ln,const char * p,int * pos)20761d06d6bSBaptiste Daroussin cell(struct tbl_node *tbl, struct tbl_row *rp,
20861d06d6bSBaptiste Daroussin 		int ln, const char *p, int *pos)
20961d06d6bSBaptiste Daroussin {
21061d06d6bSBaptiste Daroussin 	int		 i;
21161d06d6bSBaptiste Daroussin 	enum tbl_cellt	 c;
21261d06d6bSBaptiste Daroussin 
21361d06d6bSBaptiste Daroussin 	/* Handle leading vertical lines */
21461d06d6bSBaptiste Daroussin 
21561d06d6bSBaptiste Daroussin 	while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') {
21661d06d6bSBaptiste Daroussin 		if (p[*pos] == '|') {
21761d06d6bSBaptiste Daroussin 			if (rp->vert < 2)
21861d06d6bSBaptiste Daroussin 				rp->vert++;
21961d06d6bSBaptiste Daroussin 			else
22061d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_TBLLAYOUT_VERT,
2217295610fSBaptiste Daroussin 				    ln, *pos, NULL);
22261d06d6bSBaptiste Daroussin 		}
22361d06d6bSBaptiste Daroussin 		(*pos)++;
22461d06d6bSBaptiste Daroussin 	}
22561d06d6bSBaptiste Daroussin 
22661d06d6bSBaptiste Daroussin again:
22761d06d6bSBaptiste Daroussin 	while (p[*pos] == ' ' || p[*pos] == '\t')
22861d06d6bSBaptiste Daroussin 		(*pos)++;
22961d06d6bSBaptiste Daroussin 
23061d06d6bSBaptiste Daroussin 	if (p[*pos] == '.' || p[*pos] == '\0')
23161d06d6bSBaptiste Daroussin 		return;
23261d06d6bSBaptiste Daroussin 
23361d06d6bSBaptiste Daroussin 	/* Parse the column position (`c', `l', `r', ...). */
23461d06d6bSBaptiste Daroussin 
23561d06d6bSBaptiste Daroussin 	for (i = 0; i < KEYS_MAX; i++)
23661d06d6bSBaptiste Daroussin 		if (tolower((unsigned char)p[*pos]) == keys[i].name)
23761d06d6bSBaptiste Daroussin 			break;
23861d06d6bSBaptiste Daroussin 
23961d06d6bSBaptiste Daroussin 	if (i == KEYS_MAX) {
2407295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TBLLAYOUT_CHAR,
24161d06d6bSBaptiste Daroussin 		    ln, *pos, "%c", p[*pos]);
24261d06d6bSBaptiste Daroussin 		(*pos)++;
24361d06d6bSBaptiste Daroussin 		goto again;
24461d06d6bSBaptiste Daroussin 	}
24561d06d6bSBaptiste Daroussin 	c = keys[i].key;
24661d06d6bSBaptiste Daroussin 
24761d06d6bSBaptiste Daroussin 	/* Special cases of spanners. */
24861d06d6bSBaptiste Daroussin 
24961d06d6bSBaptiste Daroussin 	if (c == TBL_CELL_SPAN) {
25061d06d6bSBaptiste Daroussin 		if (rp->last == NULL)
2517295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN, ln, *pos, NULL);
25261d06d6bSBaptiste Daroussin 		else if (rp->last->pos == TBL_CELL_HORIZ ||
25361d06d6bSBaptiste Daroussin 		    rp->last->pos == TBL_CELL_DHORIZ)
25461d06d6bSBaptiste Daroussin 			c = rp->last->pos;
25561d06d6bSBaptiste Daroussin 	} else if (c == TBL_CELL_DOWN && rp == tbl->first_row)
2567295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN, ln, *pos, NULL);
25761d06d6bSBaptiste Daroussin 
25861d06d6bSBaptiste Daroussin 	(*pos)++;
25961d06d6bSBaptiste Daroussin 
26061d06d6bSBaptiste Daroussin 	/* Allocate cell then parse its modifiers. */
26161d06d6bSBaptiste Daroussin 
26261d06d6bSBaptiste Daroussin 	mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos);
26361d06d6bSBaptiste Daroussin }
26461d06d6bSBaptiste Daroussin 
26561d06d6bSBaptiste Daroussin void
tbl_layout(struct tbl_node * tbl,int ln,const char * p,int pos)26661d06d6bSBaptiste Daroussin tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos)
26761d06d6bSBaptiste Daroussin {
26861d06d6bSBaptiste Daroussin 	struct tbl_row	*rp;
26961d06d6bSBaptiste Daroussin 
27061d06d6bSBaptiste Daroussin 	rp = NULL;
27161d06d6bSBaptiste Daroussin 	for (;;) {
27261d06d6bSBaptiste Daroussin 		/* Skip whitespace before and after each cell. */
27361d06d6bSBaptiste Daroussin 
27461d06d6bSBaptiste Daroussin 		while (p[pos] == ' ' || p[pos] == '\t')
27561d06d6bSBaptiste Daroussin 			pos++;
27661d06d6bSBaptiste Daroussin 
27761d06d6bSBaptiste Daroussin 		switch (p[pos]) {
27861d06d6bSBaptiste Daroussin 		case ',':  /* Next row on this input line. */
27961d06d6bSBaptiste Daroussin 			pos++;
28061d06d6bSBaptiste Daroussin 			rp = NULL;
28161d06d6bSBaptiste Daroussin 			continue;
28261d06d6bSBaptiste Daroussin 		case '\0':  /* Next row on next input line. */
28361d06d6bSBaptiste Daroussin 			return;
28461d06d6bSBaptiste Daroussin 		case '.':  /* End of layout. */
28561d06d6bSBaptiste Daroussin 			pos++;
28661d06d6bSBaptiste Daroussin 			tbl->part = TBL_PART_DATA;
28761d06d6bSBaptiste Daroussin 
28861d06d6bSBaptiste Daroussin 			/*
28961d06d6bSBaptiste Daroussin 			 * When the layout is completely empty,
29061d06d6bSBaptiste Daroussin 			 * default to one left-justified column.
29161d06d6bSBaptiste Daroussin 			 */
29261d06d6bSBaptiste Daroussin 
29361d06d6bSBaptiste Daroussin 			if (tbl->first_row == NULL) {
29461d06d6bSBaptiste Daroussin 				tbl->first_row = tbl->last_row =
29561d06d6bSBaptiste Daroussin 				    mandoc_calloc(1, sizeof(*rp));
29661d06d6bSBaptiste Daroussin 			}
29761d06d6bSBaptiste Daroussin 			if (tbl->first_row->first == NULL) {
29861d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_TBLLAYOUT_NONE,
2997295610fSBaptiste Daroussin 				    ln, pos, NULL);
30061d06d6bSBaptiste Daroussin 				cell_alloc(tbl, tbl->first_row,
30161d06d6bSBaptiste Daroussin 				    TBL_CELL_LEFT);
30261d06d6bSBaptiste Daroussin 				if (tbl->opts.lvert < tbl->first_row->vert)
30361d06d6bSBaptiste Daroussin 					tbl->opts.lvert = tbl->first_row->vert;
30461d06d6bSBaptiste Daroussin 				return;
30561d06d6bSBaptiste Daroussin 			}
30661d06d6bSBaptiste Daroussin 
30761d06d6bSBaptiste Daroussin 			/*
30861d06d6bSBaptiste Daroussin 			 * Search for the widest line
30961d06d6bSBaptiste Daroussin 			 * along the left and right margins.
31061d06d6bSBaptiste Daroussin 			 */
31161d06d6bSBaptiste Daroussin 
31261d06d6bSBaptiste Daroussin 			for (rp = tbl->first_row; rp; rp = rp->next) {
31361d06d6bSBaptiste Daroussin 				if (tbl->opts.lvert < rp->vert)
31461d06d6bSBaptiste Daroussin 					tbl->opts.lvert = rp->vert;
31561d06d6bSBaptiste Daroussin 				if (rp->last != NULL &&
31661d06d6bSBaptiste Daroussin 				    rp->last->col + 1 == tbl->opts.cols &&
31761d06d6bSBaptiste Daroussin 				    tbl->opts.rvert < rp->last->vert)
31861d06d6bSBaptiste Daroussin 					tbl->opts.rvert = rp->last->vert;
31961d06d6bSBaptiste Daroussin 
32061d06d6bSBaptiste Daroussin 				/* If the last line is empty, drop it. */
32161d06d6bSBaptiste Daroussin 
32261d06d6bSBaptiste Daroussin 				if (rp->next != NULL &&
32361d06d6bSBaptiste Daroussin 				    rp->next->first == NULL) {
32461d06d6bSBaptiste Daroussin 					free(rp->next);
32561d06d6bSBaptiste Daroussin 					rp->next = NULL;
32661d06d6bSBaptiste Daroussin 					tbl->last_row = rp;
32761d06d6bSBaptiste Daroussin 				}
32861d06d6bSBaptiste Daroussin 			}
32961d06d6bSBaptiste Daroussin 			return;
33061d06d6bSBaptiste Daroussin 		default:  /* Cell. */
33161d06d6bSBaptiste Daroussin 			break;
33261d06d6bSBaptiste Daroussin 		}
33361d06d6bSBaptiste Daroussin 
33461d06d6bSBaptiste Daroussin 		/*
33561d06d6bSBaptiste Daroussin 		 * If the last line had at least one cell,
33661d06d6bSBaptiste Daroussin 		 * start a new one; otherwise, continue it.
33761d06d6bSBaptiste Daroussin 		 */
33861d06d6bSBaptiste Daroussin 
33961d06d6bSBaptiste Daroussin 		if (rp == NULL) {
34061d06d6bSBaptiste Daroussin 			if (tbl->last_row == NULL ||
34161d06d6bSBaptiste Daroussin 			    tbl->last_row->first != NULL) {
34261d06d6bSBaptiste Daroussin 				rp = mandoc_calloc(1, sizeof(*rp));
34361d06d6bSBaptiste Daroussin 				if (tbl->last_row)
34461d06d6bSBaptiste Daroussin 					tbl->last_row->next = rp;
34561d06d6bSBaptiste Daroussin 				else
34661d06d6bSBaptiste Daroussin 					tbl->first_row = rp;
34761d06d6bSBaptiste Daroussin 				tbl->last_row = rp;
34861d06d6bSBaptiste Daroussin 			} else
34961d06d6bSBaptiste Daroussin 				rp = tbl->last_row;
35061d06d6bSBaptiste Daroussin 		}
35161d06d6bSBaptiste Daroussin 		cell(tbl, rp, ln, p, &pos);
35261d06d6bSBaptiste Daroussin 	}
35361d06d6bSBaptiste Daroussin }
35461d06d6bSBaptiste Daroussin 
35561d06d6bSBaptiste Daroussin static struct tbl_cell *
cell_alloc(struct tbl_node * tbl,struct tbl_row * rp,enum tbl_cellt pos)35661d06d6bSBaptiste Daroussin cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
35761d06d6bSBaptiste Daroussin {
35861d06d6bSBaptiste Daroussin 	struct tbl_cell	*p, *pp;
35961d06d6bSBaptiste Daroussin 
36061d06d6bSBaptiste Daroussin 	p = mandoc_calloc(1, sizeof(*p));
36161d06d6bSBaptiste Daroussin 	p->spacing = SIZE_MAX;
362*6d38604fSBaptiste Daroussin 	p->font = ESCAPE_FONTROMAN;
36361d06d6bSBaptiste Daroussin 	p->pos = pos;
36461d06d6bSBaptiste Daroussin 
36561d06d6bSBaptiste Daroussin 	if ((pp = rp->last) != NULL) {
36661d06d6bSBaptiste Daroussin 		pp->next = p;
36761d06d6bSBaptiste Daroussin 		p->col = pp->col + 1;
36861d06d6bSBaptiste Daroussin 	} else
36961d06d6bSBaptiste Daroussin 		rp->first = p;
37061d06d6bSBaptiste Daroussin 	rp->last = p;
37161d06d6bSBaptiste Daroussin 
37261d06d6bSBaptiste Daroussin 	if (tbl->opts.cols <= p->col)
37361d06d6bSBaptiste Daroussin 		tbl->opts.cols = p->col + 1;
37461d06d6bSBaptiste Daroussin 
37561d06d6bSBaptiste Daroussin 	return p;
37661d06d6bSBaptiste Daroussin }
377