1*7295610fSBaptiste Daroussin /* $Id: tbl_layout.c,v 1.48 2018/12/14 05:18:03 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 361d06d6bSBaptiste Daroussin * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 461d06d6bSBaptiste Daroussin * Copyright (c) 2012, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> 561d06d6bSBaptiste Daroussin * 661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 961d06d6bSBaptiste Daroussin * 1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1761d06d6bSBaptiste Daroussin */ 1861d06d6bSBaptiste Daroussin #include "config.h" 1961d06d6bSBaptiste Daroussin 2061d06d6bSBaptiste Daroussin #include <sys/types.h> 2161d06d6bSBaptiste Daroussin 2261d06d6bSBaptiste Daroussin #include <ctype.h> 2361d06d6bSBaptiste Daroussin #include <stdint.h> 24*7295610fSBaptiste Daroussin #include <stdio.h> 2561d06d6bSBaptiste Daroussin #include <stdlib.h> 2661d06d6bSBaptiste Daroussin #include <string.h> 2761d06d6bSBaptiste Daroussin #include <time.h> 2861d06d6bSBaptiste Daroussin 2961d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 30*7295610fSBaptiste Daroussin #include "mandoc.h" 31*7295610fSBaptiste Daroussin #include "tbl.h" 3261d06d6bSBaptiste Daroussin #include "libmandoc.h" 33*7295610fSBaptiste Daroussin #include "tbl_int.h" 3461d06d6bSBaptiste Daroussin 3561d06d6bSBaptiste Daroussin struct tbl_phrase { 3661d06d6bSBaptiste Daroussin char name; 3761d06d6bSBaptiste Daroussin enum tbl_cellt key; 3861d06d6bSBaptiste Daroussin }; 3961d06d6bSBaptiste Daroussin 4061d06d6bSBaptiste Daroussin static const struct tbl_phrase keys[] = { 4161d06d6bSBaptiste Daroussin { 'c', TBL_CELL_CENTRE }, 4261d06d6bSBaptiste Daroussin { 'r', TBL_CELL_RIGHT }, 4361d06d6bSBaptiste Daroussin { 'l', TBL_CELL_LEFT }, 4461d06d6bSBaptiste Daroussin { 'n', TBL_CELL_NUMBER }, 4561d06d6bSBaptiste Daroussin { 's', TBL_CELL_SPAN }, 4661d06d6bSBaptiste Daroussin { 'a', TBL_CELL_LONG }, 4761d06d6bSBaptiste Daroussin { '^', TBL_CELL_DOWN }, 4861d06d6bSBaptiste Daroussin { '-', TBL_CELL_HORIZ }, 4961d06d6bSBaptiste Daroussin { '_', TBL_CELL_HORIZ }, 5061d06d6bSBaptiste Daroussin { '=', TBL_CELL_DHORIZ } 5161d06d6bSBaptiste Daroussin }; 5261d06d6bSBaptiste Daroussin 5361d06d6bSBaptiste Daroussin #define KEYS_MAX ((int)(sizeof(keys)/sizeof(keys[0]))) 5461d06d6bSBaptiste Daroussin 5561d06d6bSBaptiste Daroussin static void mods(struct tbl_node *, struct tbl_cell *, 5661d06d6bSBaptiste Daroussin int, const char *, int *); 5761d06d6bSBaptiste Daroussin static void cell(struct tbl_node *, struct tbl_row *, 5861d06d6bSBaptiste Daroussin int, const char *, int *); 5961d06d6bSBaptiste Daroussin static struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *, 6061d06d6bSBaptiste Daroussin enum tbl_cellt); 6161d06d6bSBaptiste Daroussin 6261d06d6bSBaptiste Daroussin 6361d06d6bSBaptiste Daroussin static void 6461d06d6bSBaptiste Daroussin mods(struct tbl_node *tbl, struct tbl_cell *cp, 6561d06d6bSBaptiste Daroussin int ln, const char *p, int *pos) 6661d06d6bSBaptiste Daroussin { 6761d06d6bSBaptiste Daroussin char *endptr; 6861d06d6bSBaptiste Daroussin size_t sz; 6961d06d6bSBaptiste Daroussin 7061d06d6bSBaptiste Daroussin mod: 7161d06d6bSBaptiste Daroussin while (p[*pos] == ' ' || p[*pos] == '\t') 7261d06d6bSBaptiste Daroussin (*pos)++; 7361d06d6bSBaptiste Daroussin 7461d06d6bSBaptiste Daroussin /* Row delimiters and cell specifiers end modifier lists. */ 7561d06d6bSBaptiste Daroussin 7661d06d6bSBaptiste Daroussin if (strchr(".,-=^_ACLNRSaclnrs", p[*pos]) != NULL) 7761d06d6bSBaptiste Daroussin return; 7861d06d6bSBaptiste Daroussin 7961d06d6bSBaptiste Daroussin /* Throw away parenthesised expression. */ 8061d06d6bSBaptiste Daroussin 8161d06d6bSBaptiste Daroussin if ('(' == p[*pos]) { 8261d06d6bSBaptiste Daroussin (*pos)++; 8361d06d6bSBaptiste Daroussin while (p[*pos] && ')' != p[*pos]) 8461d06d6bSBaptiste Daroussin (*pos)++; 8561d06d6bSBaptiste Daroussin if (')' == p[*pos]) { 8661d06d6bSBaptiste Daroussin (*pos)++; 8761d06d6bSBaptiste Daroussin goto mod; 8861d06d6bSBaptiste Daroussin } 89*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLLAYOUT_PAR, ln, *pos, NULL); 9061d06d6bSBaptiste Daroussin return; 9161d06d6bSBaptiste Daroussin } 9261d06d6bSBaptiste Daroussin 9361d06d6bSBaptiste Daroussin /* Parse numerical spacing from modifier string. */ 9461d06d6bSBaptiste Daroussin 9561d06d6bSBaptiste Daroussin if (isdigit((unsigned char)p[*pos])) { 9661d06d6bSBaptiste Daroussin cp->spacing = strtoull(p + *pos, &endptr, 10); 9761d06d6bSBaptiste Daroussin *pos = endptr - p; 9861d06d6bSBaptiste Daroussin goto mod; 9961d06d6bSBaptiste Daroussin } 10061d06d6bSBaptiste Daroussin 10161d06d6bSBaptiste Daroussin switch (tolower((unsigned char)p[(*pos)++])) { 10261d06d6bSBaptiste Daroussin case 'b': 10361d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_BOLD; 10461d06d6bSBaptiste Daroussin goto mod; 10561d06d6bSBaptiste Daroussin case 'd': 10661d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_BALIGN; 10761d06d6bSBaptiste Daroussin goto mod; 10861d06d6bSBaptiste Daroussin case 'e': 10961d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_EQUAL; 11061d06d6bSBaptiste Daroussin goto mod; 11161d06d6bSBaptiste Daroussin case 'f': 11261d06d6bSBaptiste Daroussin break; 11361d06d6bSBaptiste Daroussin case 'i': 11461d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_ITALIC; 11561d06d6bSBaptiste Daroussin goto mod; 11661d06d6bSBaptiste Daroussin case 'm': 117*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, ln, *pos, "m"); 11861d06d6bSBaptiste Daroussin goto mod; 11961d06d6bSBaptiste Daroussin case 'p': 12061d06d6bSBaptiste Daroussin case 'v': 12161d06d6bSBaptiste Daroussin if (p[*pos] == '-' || p[*pos] == '+') 12261d06d6bSBaptiste Daroussin (*pos)++; 12361d06d6bSBaptiste Daroussin while (isdigit((unsigned char)p[*pos])) 12461d06d6bSBaptiste Daroussin (*pos)++; 12561d06d6bSBaptiste Daroussin goto mod; 12661d06d6bSBaptiste Daroussin case 't': 12761d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_TALIGN; 12861d06d6bSBaptiste Daroussin goto mod; 12961d06d6bSBaptiste Daroussin case 'u': 13061d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_UP; 13161d06d6bSBaptiste Daroussin goto mod; 13261d06d6bSBaptiste Daroussin case 'w': 13361d06d6bSBaptiste Daroussin sz = 0; 13461d06d6bSBaptiste Daroussin if (p[*pos] == '(') { 13561d06d6bSBaptiste Daroussin (*pos)++; 13661d06d6bSBaptiste Daroussin while (p[*pos + sz] != '\0' && p[*pos + sz] != ')') 13761d06d6bSBaptiste Daroussin sz++; 13861d06d6bSBaptiste Daroussin } else 13961d06d6bSBaptiste Daroussin while (isdigit((unsigned char)p[*pos + sz])) 14061d06d6bSBaptiste Daroussin sz++; 14161d06d6bSBaptiste Daroussin if (sz) { 14261d06d6bSBaptiste Daroussin free(cp->wstr); 14361d06d6bSBaptiste Daroussin cp->wstr = mandoc_strndup(p + *pos, sz); 14461d06d6bSBaptiste Daroussin *pos += sz; 14561d06d6bSBaptiste Daroussin if (p[*pos] == ')') 14661d06d6bSBaptiste Daroussin (*pos)++; 14761d06d6bSBaptiste Daroussin } 14861d06d6bSBaptiste Daroussin goto mod; 14961d06d6bSBaptiste Daroussin case 'x': 15061d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_WMAX; 15161d06d6bSBaptiste Daroussin goto mod; 15261d06d6bSBaptiste Daroussin case 'z': 15361d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_WIGN; 15461d06d6bSBaptiste Daroussin goto mod; 15561d06d6bSBaptiste Daroussin case '|': 15661d06d6bSBaptiste Daroussin if (cp->vert < 2) 15761d06d6bSBaptiste Daroussin cp->vert++; 15861d06d6bSBaptiste Daroussin else 15961d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLLAYOUT_VERT, 160*7295610fSBaptiste Daroussin ln, *pos - 1, NULL); 16161d06d6bSBaptiste Daroussin goto mod; 16261d06d6bSBaptiste Daroussin default: 163*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLLAYOUT_CHAR, 16461d06d6bSBaptiste Daroussin ln, *pos - 1, "%c", p[*pos - 1]); 16561d06d6bSBaptiste Daroussin goto mod; 16661d06d6bSBaptiste Daroussin } 16761d06d6bSBaptiste Daroussin 16861d06d6bSBaptiste Daroussin /* Ignore parenthised font names for now. */ 16961d06d6bSBaptiste Daroussin 17061d06d6bSBaptiste Daroussin if (p[*pos] == '(') 17161d06d6bSBaptiste Daroussin goto mod; 17261d06d6bSBaptiste Daroussin 17361d06d6bSBaptiste Daroussin /* Support only one-character font-names for now. */ 17461d06d6bSBaptiste Daroussin 17561d06d6bSBaptiste Daroussin if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) { 176*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_FT_BAD, 17761d06d6bSBaptiste Daroussin ln, *pos, "TS %s", p + *pos - 1); 17861d06d6bSBaptiste Daroussin if (p[*pos] != '\0') 17961d06d6bSBaptiste Daroussin (*pos)++; 18061d06d6bSBaptiste Daroussin if (p[*pos] != '\0') 18161d06d6bSBaptiste Daroussin (*pos)++; 18261d06d6bSBaptiste Daroussin goto mod; 18361d06d6bSBaptiste Daroussin } 18461d06d6bSBaptiste Daroussin 18561d06d6bSBaptiste Daroussin switch (p[(*pos)++]) { 18661d06d6bSBaptiste Daroussin case '3': 18761d06d6bSBaptiste Daroussin case 'B': 18861d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_BOLD; 18961d06d6bSBaptiste Daroussin goto mod; 19061d06d6bSBaptiste Daroussin case '2': 19161d06d6bSBaptiste Daroussin case 'I': 19261d06d6bSBaptiste Daroussin cp->flags |= TBL_CELL_ITALIC; 19361d06d6bSBaptiste Daroussin goto mod; 19461d06d6bSBaptiste Daroussin case '1': 19561d06d6bSBaptiste Daroussin case 'R': 19661d06d6bSBaptiste Daroussin goto mod; 19761d06d6bSBaptiste Daroussin default: 198*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_FT_BAD, 19961d06d6bSBaptiste Daroussin ln, *pos - 1, "TS f%c", p[*pos - 1]); 20061d06d6bSBaptiste Daroussin goto mod; 20161d06d6bSBaptiste Daroussin } 20261d06d6bSBaptiste Daroussin } 20361d06d6bSBaptiste Daroussin 20461d06d6bSBaptiste Daroussin static void 20561d06d6bSBaptiste Daroussin cell(struct tbl_node *tbl, struct tbl_row *rp, 20661d06d6bSBaptiste Daroussin int ln, const char *p, int *pos) 20761d06d6bSBaptiste Daroussin { 20861d06d6bSBaptiste Daroussin int i; 20961d06d6bSBaptiste Daroussin enum tbl_cellt c; 21061d06d6bSBaptiste Daroussin 21161d06d6bSBaptiste Daroussin /* Handle leading vertical lines */ 21261d06d6bSBaptiste Daroussin 21361d06d6bSBaptiste Daroussin while (p[*pos] == ' ' || p[*pos] == '\t' || p[*pos] == '|') { 21461d06d6bSBaptiste Daroussin if (p[*pos] == '|') { 21561d06d6bSBaptiste Daroussin if (rp->vert < 2) 21661d06d6bSBaptiste Daroussin rp->vert++; 21761d06d6bSBaptiste Daroussin else 21861d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLLAYOUT_VERT, 219*7295610fSBaptiste Daroussin ln, *pos, NULL); 22061d06d6bSBaptiste Daroussin } 22161d06d6bSBaptiste Daroussin (*pos)++; 22261d06d6bSBaptiste Daroussin } 22361d06d6bSBaptiste Daroussin 22461d06d6bSBaptiste Daroussin again: 22561d06d6bSBaptiste Daroussin while (p[*pos] == ' ' || p[*pos] == '\t') 22661d06d6bSBaptiste Daroussin (*pos)++; 22761d06d6bSBaptiste Daroussin 22861d06d6bSBaptiste Daroussin if (p[*pos] == '.' || p[*pos] == '\0') 22961d06d6bSBaptiste Daroussin return; 23061d06d6bSBaptiste Daroussin 23161d06d6bSBaptiste Daroussin /* Parse the column position (`c', `l', `r', ...). */ 23261d06d6bSBaptiste Daroussin 23361d06d6bSBaptiste Daroussin for (i = 0; i < KEYS_MAX; i++) 23461d06d6bSBaptiste Daroussin if (tolower((unsigned char)p[*pos]) == keys[i].name) 23561d06d6bSBaptiste Daroussin break; 23661d06d6bSBaptiste Daroussin 23761d06d6bSBaptiste Daroussin if (i == KEYS_MAX) { 238*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLLAYOUT_CHAR, 23961d06d6bSBaptiste Daroussin ln, *pos, "%c", p[*pos]); 24061d06d6bSBaptiste Daroussin (*pos)++; 24161d06d6bSBaptiste Daroussin goto again; 24261d06d6bSBaptiste Daroussin } 24361d06d6bSBaptiste Daroussin c = keys[i].key; 24461d06d6bSBaptiste Daroussin 24561d06d6bSBaptiste Daroussin /* Special cases of spanners. */ 24661d06d6bSBaptiste Daroussin 24761d06d6bSBaptiste Daroussin if (c == TBL_CELL_SPAN) { 24861d06d6bSBaptiste Daroussin if (rp->last == NULL) 249*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLLAYOUT_SPAN, ln, *pos, NULL); 25061d06d6bSBaptiste Daroussin else if (rp->last->pos == TBL_CELL_HORIZ || 25161d06d6bSBaptiste Daroussin rp->last->pos == TBL_CELL_DHORIZ) 25261d06d6bSBaptiste Daroussin c = rp->last->pos; 25361d06d6bSBaptiste Daroussin } else if (c == TBL_CELL_DOWN && rp == tbl->first_row) 254*7295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLLAYOUT_DOWN, ln, *pos, NULL); 25561d06d6bSBaptiste Daroussin 25661d06d6bSBaptiste Daroussin (*pos)++; 25761d06d6bSBaptiste Daroussin 25861d06d6bSBaptiste Daroussin /* Allocate cell then parse its modifiers. */ 25961d06d6bSBaptiste Daroussin 26061d06d6bSBaptiste Daroussin mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos); 26161d06d6bSBaptiste Daroussin } 26261d06d6bSBaptiste Daroussin 26361d06d6bSBaptiste Daroussin void 26461d06d6bSBaptiste Daroussin tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos) 26561d06d6bSBaptiste Daroussin { 26661d06d6bSBaptiste Daroussin struct tbl_row *rp; 26761d06d6bSBaptiste Daroussin 26861d06d6bSBaptiste Daroussin rp = NULL; 26961d06d6bSBaptiste Daroussin for (;;) { 27061d06d6bSBaptiste Daroussin /* Skip whitespace before and after each cell. */ 27161d06d6bSBaptiste Daroussin 27261d06d6bSBaptiste Daroussin while (p[pos] == ' ' || p[pos] == '\t') 27361d06d6bSBaptiste Daroussin pos++; 27461d06d6bSBaptiste Daroussin 27561d06d6bSBaptiste Daroussin switch (p[pos]) { 27661d06d6bSBaptiste Daroussin case ',': /* Next row on this input line. */ 27761d06d6bSBaptiste Daroussin pos++; 27861d06d6bSBaptiste Daroussin rp = NULL; 27961d06d6bSBaptiste Daroussin continue; 28061d06d6bSBaptiste Daroussin case '\0': /* Next row on next input line. */ 28161d06d6bSBaptiste Daroussin return; 28261d06d6bSBaptiste Daroussin case '.': /* End of layout. */ 28361d06d6bSBaptiste Daroussin pos++; 28461d06d6bSBaptiste Daroussin tbl->part = TBL_PART_DATA; 28561d06d6bSBaptiste Daroussin 28661d06d6bSBaptiste Daroussin /* 28761d06d6bSBaptiste Daroussin * When the layout is completely empty, 28861d06d6bSBaptiste Daroussin * default to one left-justified column. 28961d06d6bSBaptiste Daroussin */ 29061d06d6bSBaptiste Daroussin 29161d06d6bSBaptiste Daroussin if (tbl->first_row == NULL) { 29261d06d6bSBaptiste Daroussin tbl->first_row = tbl->last_row = 29361d06d6bSBaptiste Daroussin mandoc_calloc(1, sizeof(*rp)); 29461d06d6bSBaptiste Daroussin } 29561d06d6bSBaptiste Daroussin if (tbl->first_row->first == NULL) { 29661d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLLAYOUT_NONE, 297*7295610fSBaptiste Daroussin ln, pos, NULL); 29861d06d6bSBaptiste Daroussin cell_alloc(tbl, tbl->first_row, 29961d06d6bSBaptiste Daroussin TBL_CELL_LEFT); 30061d06d6bSBaptiste Daroussin if (tbl->opts.lvert < tbl->first_row->vert) 30161d06d6bSBaptiste Daroussin tbl->opts.lvert = tbl->first_row->vert; 30261d06d6bSBaptiste Daroussin return; 30361d06d6bSBaptiste Daroussin } 30461d06d6bSBaptiste Daroussin 30561d06d6bSBaptiste Daroussin /* 30661d06d6bSBaptiste Daroussin * Search for the widest line 30761d06d6bSBaptiste Daroussin * along the left and right margins. 30861d06d6bSBaptiste Daroussin */ 30961d06d6bSBaptiste Daroussin 31061d06d6bSBaptiste Daroussin for (rp = tbl->first_row; rp; rp = rp->next) { 31161d06d6bSBaptiste Daroussin if (tbl->opts.lvert < rp->vert) 31261d06d6bSBaptiste Daroussin tbl->opts.lvert = rp->vert; 31361d06d6bSBaptiste Daroussin if (rp->last != NULL && 31461d06d6bSBaptiste Daroussin rp->last->col + 1 == tbl->opts.cols && 31561d06d6bSBaptiste Daroussin tbl->opts.rvert < rp->last->vert) 31661d06d6bSBaptiste Daroussin tbl->opts.rvert = rp->last->vert; 31761d06d6bSBaptiste Daroussin 31861d06d6bSBaptiste Daroussin /* If the last line is empty, drop it. */ 31961d06d6bSBaptiste Daroussin 32061d06d6bSBaptiste Daroussin if (rp->next != NULL && 32161d06d6bSBaptiste Daroussin rp->next->first == NULL) { 32261d06d6bSBaptiste Daroussin free(rp->next); 32361d06d6bSBaptiste Daroussin rp->next = NULL; 32461d06d6bSBaptiste Daroussin tbl->last_row = rp; 32561d06d6bSBaptiste Daroussin } 32661d06d6bSBaptiste Daroussin } 32761d06d6bSBaptiste Daroussin return; 32861d06d6bSBaptiste Daroussin default: /* Cell. */ 32961d06d6bSBaptiste Daroussin break; 33061d06d6bSBaptiste Daroussin } 33161d06d6bSBaptiste Daroussin 33261d06d6bSBaptiste Daroussin /* 33361d06d6bSBaptiste Daroussin * If the last line had at least one cell, 33461d06d6bSBaptiste Daroussin * start a new one; otherwise, continue it. 33561d06d6bSBaptiste Daroussin */ 33661d06d6bSBaptiste Daroussin 33761d06d6bSBaptiste Daroussin if (rp == NULL) { 33861d06d6bSBaptiste Daroussin if (tbl->last_row == NULL || 33961d06d6bSBaptiste Daroussin tbl->last_row->first != NULL) { 34061d06d6bSBaptiste Daroussin rp = mandoc_calloc(1, sizeof(*rp)); 34161d06d6bSBaptiste Daroussin if (tbl->last_row) 34261d06d6bSBaptiste Daroussin tbl->last_row->next = rp; 34361d06d6bSBaptiste Daroussin else 34461d06d6bSBaptiste Daroussin tbl->first_row = rp; 34561d06d6bSBaptiste Daroussin tbl->last_row = rp; 34661d06d6bSBaptiste Daroussin } else 34761d06d6bSBaptiste Daroussin rp = tbl->last_row; 34861d06d6bSBaptiste Daroussin } 34961d06d6bSBaptiste Daroussin cell(tbl, rp, ln, p, &pos); 35061d06d6bSBaptiste Daroussin } 35161d06d6bSBaptiste Daroussin } 35261d06d6bSBaptiste Daroussin 35361d06d6bSBaptiste Daroussin static struct tbl_cell * 35461d06d6bSBaptiste Daroussin cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) 35561d06d6bSBaptiste Daroussin { 35661d06d6bSBaptiste Daroussin struct tbl_cell *p, *pp; 35761d06d6bSBaptiste Daroussin 35861d06d6bSBaptiste Daroussin p = mandoc_calloc(1, sizeof(*p)); 35961d06d6bSBaptiste Daroussin p->spacing = SIZE_MAX; 36061d06d6bSBaptiste Daroussin p->pos = pos; 36161d06d6bSBaptiste Daroussin 36261d06d6bSBaptiste Daroussin if ((pp = rp->last) != NULL) { 36361d06d6bSBaptiste Daroussin pp->next = p; 36461d06d6bSBaptiste Daroussin p->col = pp->col + 1; 36561d06d6bSBaptiste Daroussin } else 36661d06d6bSBaptiste Daroussin rp->first = p; 36761d06d6bSBaptiste Daroussin rp->last = p; 36861d06d6bSBaptiste Daroussin 36961d06d6bSBaptiste Daroussin if (tbl->opts.cols <= p->col) 37061d06d6bSBaptiste Daroussin tbl->opts.cols = p->col + 1; 37161d06d6bSBaptiste Daroussin 37261d06d6bSBaptiste Daroussin return p; 37361d06d6bSBaptiste Daroussin } 374