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