1*61d06d6bSBaptiste Daroussin /* $Id: tbl_data.c,v 1.45 2017/07/08 17:52:50 schwarze Exp $ */ 2*61d06d6bSBaptiste Daroussin /* 3*61d06d6bSBaptiste Daroussin * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4*61d06d6bSBaptiste Daroussin * Copyright (c) 2011, 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 <assert.h> 23*61d06d6bSBaptiste Daroussin #include <ctype.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 static void getdata(struct tbl_node *, struct tbl_span *, 34*61d06d6bSBaptiste Daroussin int, const char *, int *); 35*61d06d6bSBaptiste Daroussin static struct tbl_span *newspan(struct tbl_node *, int, 36*61d06d6bSBaptiste Daroussin struct tbl_row *); 37*61d06d6bSBaptiste Daroussin 38*61d06d6bSBaptiste Daroussin 39*61d06d6bSBaptiste Daroussin static void 40*61d06d6bSBaptiste Daroussin getdata(struct tbl_node *tbl, struct tbl_span *dp, 41*61d06d6bSBaptiste Daroussin int ln, const char *p, int *pos) 42*61d06d6bSBaptiste Daroussin { 43*61d06d6bSBaptiste Daroussin struct tbl_dat *dat; 44*61d06d6bSBaptiste Daroussin struct tbl_cell *cp; 45*61d06d6bSBaptiste Daroussin int sv; 46*61d06d6bSBaptiste Daroussin 47*61d06d6bSBaptiste Daroussin /* Advance to the next layout cell, skipping spanners. */ 48*61d06d6bSBaptiste Daroussin 49*61d06d6bSBaptiste Daroussin cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next; 50*61d06d6bSBaptiste Daroussin while (cp != NULL && cp->pos == TBL_CELL_SPAN) 51*61d06d6bSBaptiste Daroussin cp = cp->next; 52*61d06d6bSBaptiste Daroussin 53*61d06d6bSBaptiste Daroussin /* 54*61d06d6bSBaptiste Daroussin * If the current layout row is out of cells, allocate 55*61d06d6bSBaptiste Daroussin * a new cell if another row of the table has at least 56*61d06d6bSBaptiste Daroussin * this number of columns, or discard the input if we 57*61d06d6bSBaptiste Daroussin * are beyond the last column of the table as a whole. 58*61d06d6bSBaptiste Daroussin */ 59*61d06d6bSBaptiste Daroussin 60*61d06d6bSBaptiste Daroussin if (cp == NULL) { 61*61d06d6bSBaptiste Daroussin if (dp->layout->last->col + 1 < dp->opts->cols) { 62*61d06d6bSBaptiste Daroussin cp = mandoc_calloc(1, sizeof(*cp)); 63*61d06d6bSBaptiste Daroussin cp->pos = TBL_CELL_LEFT; 64*61d06d6bSBaptiste Daroussin dp->layout->last->next = cp; 65*61d06d6bSBaptiste Daroussin cp->col = dp->layout->last->col + 1; 66*61d06d6bSBaptiste Daroussin dp->layout->last = cp; 67*61d06d6bSBaptiste Daroussin } else { 68*61d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLDATA_EXTRA, tbl->parse, 69*61d06d6bSBaptiste Daroussin ln, *pos, p + *pos); 70*61d06d6bSBaptiste Daroussin while (p[*pos]) 71*61d06d6bSBaptiste Daroussin (*pos)++; 72*61d06d6bSBaptiste Daroussin return; 73*61d06d6bSBaptiste Daroussin } 74*61d06d6bSBaptiste Daroussin } 75*61d06d6bSBaptiste Daroussin 76*61d06d6bSBaptiste Daroussin dat = mandoc_calloc(1, sizeof(*dat)); 77*61d06d6bSBaptiste Daroussin dat->layout = cp; 78*61d06d6bSBaptiste Daroussin dat->pos = TBL_DATA_NONE; 79*61d06d6bSBaptiste Daroussin dat->spans = 0; 80*61d06d6bSBaptiste Daroussin for (cp = cp->next; cp != NULL; cp = cp->next) 81*61d06d6bSBaptiste Daroussin if (cp->pos == TBL_CELL_SPAN) 82*61d06d6bSBaptiste Daroussin dat->spans++; 83*61d06d6bSBaptiste Daroussin else 84*61d06d6bSBaptiste Daroussin break; 85*61d06d6bSBaptiste Daroussin 86*61d06d6bSBaptiste Daroussin if (dp->last == NULL) 87*61d06d6bSBaptiste Daroussin dp->first = dat; 88*61d06d6bSBaptiste Daroussin else 89*61d06d6bSBaptiste Daroussin dp->last->next = dat; 90*61d06d6bSBaptiste Daroussin dp->last = dat; 91*61d06d6bSBaptiste Daroussin 92*61d06d6bSBaptiste Daroussin sv = *pos; 93*61d06d6bSBaptiste Daroussin while (p[*pos] && p[*pos] != tbl->opts.tab) 94*61d06d6bSBaptiste Daroussin (*pos)++; 95*61d06d6bSBaptiste Daroussin 96*61d06d6bSBaptiste Daroussin /* 97*61d06d6bSBaptiste Daroussin * Check for a continued-data scope opening. This consists of a 98*61d06d6bSBaptiste Daroussin * trailing `T{' at the end of the line. Subsequent lines, 99*61d06d6bSBaptiste Daroussin * until a standalone `T}', are included in our cell. 100*61d06d6bSBaptiste Daroussin */ 101*61d06d6bSBaptiste Daroussin 102*61d06d6bSBaptiste Daroussin if (*pos - sv == 2 && p[sv] == 'T' && p[sv + 1] == '{') { 103*61d06d6bSBaptiste Daroussin tbl->part = TBL_PART_CDATA; 104*61d06d6bSBaptiste Daroussin return; 105*61d06d6bSBaptiste Daroussin } 106*61d06d6bSBaptiste Daroussin 107*61d06d6bSBaptiste Daroussin dat->string = mandoc_strndup(p + sv, *pos - sv); 108*61d06d6bSBaptiste Daroussin 109*61d06d6bSBaptiste Daroussin if (p[*pos]) 110*61d06d6bSBaptiste Daroussin (*pos)++; 111*61d06d6bSBaptiste Daroussin 112*61d06d6bSBaptiste Daroussin if ( ! strcmp(dat->string, "_")) 113*61d06d6bSBaptiste Daroussin dat->pos = TBL_DATA_HORIZ; 114*61d06d6bSBaptiste Daroussin else if ( ! strcmp(dat->string, "=")) 115*61d06d6bSBaptiste Daroussin dat->pos = TBL_DATA_DHORIZ; 116*61d06d6bSBaptiste Daroussin else if ( ! strcmp(dat->string, "\\_")) 117*61d06d6bSBaptiste Daroussin dat->pos = TBL_DATA_NHORIZ; 118*61d06d6bSBaptiste Daroussin else if ( ! strcmp(dat->string, "\\=")) 119*61d06d6bSBaptiste Daroussin dat->pos = TBL_DATA_NDHORIZ; 120*61d06d6bSBaptiste Daroussin else 121*61d06d6bSBaptiste Daroussin dat->pos = TBL_DATA_DATA; 122*61d06d6bSBaptiste Daroussin 123*61d06d6bSBaptiste Daroussin if ((dat->layout->pos == TBL_CELL_HORIZ || 124*61d06d6bSBaptiste Daroussin dat->layout->pos == TBL_CELL_DHORIZ || 125*61d06d6bSBaptiste Daroussin dat->layout->pos == TBL_CELL_DOWN) && 126*61d06d6bSBaptiste Daroussin dat->pos == TBL_DATA_DATA && *dat->string != '\0') 127*61d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLDATA_SPAN, 128*61d06d6bSBaptiste Daroussin tbl->parse, ln, sv, dat->string); 129*61d06d6bSBaptiste Daroussin } 130*61d06d6bSBaptiste Daroussin 131*61d06d6bSBaptiste Daroussin void 132*61d06d6bSBaptiste Daroussin tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos) 133*61d06d6bSBaptiste Daroussin { 134*61d06d6bSBaptiste Daroussin struct tbl_dat *dat; 135*61d06d6bSBaptiste Daroussin size_t sz; 136*61d06d6bSBaptiste Daroussin 137*61d06d6bSBaptiste Daroussin dat = tbl->last_span->last; 138*61d06d6bSBaptiste Daroussin 139*61d06d6bSBaptiste Daroussin if (p[pos] == 'T' && p[pos + 1] == '}') { 140*61d06d6bSBaptiste Daroussin pos += 2; 141*61d06d6bSBaptiste Daroussin if (p[pos] == tbl->opts.tab) { 142*61d06d6bSBaptiste Daroussin tbl->part = TBL_PART_DATA; 143*61d06d6bSBaptiste Daroussin pos++; 144*61d06d6bSBaptiste Daroussin while (p[pos] != '\0') 145*61d06d6bSBaptiste Daroussin getdata(tbl, tbl->last_span, ln, p, &pos); 146*61d06d6bSBaptiste Daroussin return; 147*61d06d6bSBaptiste Daroussin } else if (p[pos] == '\0') { 148*61d06d6bSBaptiste Daroussin tbl->part = TBL_PART_DATA; 149*61d06d6bSBaptiste Daroussin return; 150*61d06d6bSBaptiste Daroussin } 151*61d06d6bSBaptiste Daroussin 152*61d06d6bSBaptiste Daroussin /* Fallthrough: T} is part of a word. */ 153*61d06d6bSBaptiste Daroussin } 154*61d06d6bSBaptiste Daroussin 155*61d06d6bSBaptiste Daroussin dat->pos = TBL_DATA_DATA; 156*61d06d6bSBaptiste Daroussin dat->block = 1; 157*61d06d6bSBaptiste Daroussin 158*61d06d6bSBaptiste Daroussin if (dat->string != NULL) { 159*61d06d6bSBaptiste Daroussin sz = strlen(p + pos) + strlen(dat->string) + 2; 160*61d06d6bSBaptiste Daroussin dat->string = mandoc_realloc(dat->string, sz); 161*61d06d6bSBaptiste Daroussin (void)strlcat(dat->string, " ", sz); 162*61d06d6bSBaptiste Daroussin (void)strlcat(dat->string, p + pos, sz); 163*61d06d6bSBaptiste Daroussin } else 164*61d06d6bSBaptiste Daroussin dat->string = mandoc_strdup(p + pos); 165*61d06d6bSBaptiste Daroussin 166*61d06d6bSBaptiste Daroussin if (dat->layout->pos == TBL_CELL_DOWN) 167*61d06d6bSBaptiste Daroussin mandoc_msg(MANDOCERR_TBLDATA_SPAN, tbl->parse, 168*61d06d6bSBaptiste Daroussin ln, pos, dat->string); 169*61d06d6bSBaptiste Daroussin } 170*61d06d6bSBaptiste Daroussin 171*61d06d6bSBaptiste Daroussin static struct tbl_span * 172*61d06d6bSBaptiste Daroussin newspan(struct tbl_node *tbl, int line, struct tbl_row *rp) 173*61d06d6bSBaptiste Daroussin { 174*61d06d6bSBaptiste Daroussin struct tbl_span *dp; 175*61d06d6bSBaptiste Daroussin 176*61d06d6bSBaptiste Daroussin dp = mandoc_calloc(1, sizeof(*dp)); 177*61d06d6bSBaptiste Daroussin dp->line = line; 178*61d06d6bSBaptiste Daroussin dp->opts = &tbl->opts; 179*61d06d6bSBaptiste Daroussin dp->layout = rp; 180*61d06d6bSBaptiste Daroussin dp->prev = tbl->last_span; 181*61d06d6bSBaptiste Daroussin 182*61d06d6bSBaptiste Daroussin if (dp->prev == NULL) { 183*61d06d6bSBaptiste Daroussin tbl->first_span = dp; 184*61d06d6bSBaptiste Daroussin tbl->current_span = NULL; 185*61d06d6bSBaptiste Daroussin } else 186*61d06d6bSBaptiste Daroussin dp->prev->next = dp; 187*61d06d6bSBaptiste Daroussin tbl->last_span = dp; 188*61d06d6bSBaptiste Daroussin 189*61d06d6bSBaptiste Daroussin return dp; 190*61d06d6bSBaptiste Daroussin } 191*61d06d6bSBaptiste Daroussin 192*61d06d6bSBaptiste Daroussin void 193*61d06d6bSBaptiste Daroussin tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos) 194*61d06d6bSBaptiste Daroussin { 195*61d06d6bSBaptiste Daroussin struct tbl_row *rp; 196*61d06d6bSBaptiste Daroussin struct tbl_cell *cp; 197*61d06d6bSBaptiste Daroussin struct tbl_span *sp; 198*61d06d6bSBaptiste Daroussin 199*61d06d6bSBaptiste Daroussin rp = (sp = tbl->last_span) == NULL ? tbl->first_row : 200*61d06d6bSBaptiste Daroussin sp->pos == TBL_SPAN_DATA && sp->layout->next != NULL ? 201*61d06d6bSBaptiste Daroussin sp->layout->next : sp->layout; 202*61d06d6bSBaptiste Daroussin 203*61d06d6bSBaptiste Daroussin assert(rp != NULL); 204*61d06d6bSBaptiste Daroussin 205*61d06d6bSBaptiste Daroussin if ( ! strcmp(p, "_")) { 206*61d06d6bSBaptiste Daroussin sp = newspan(tbl, ln, rp); 207*61d06d6bSBaptiste Daroussin sp->pos = TBL_SPAN_HORIZ; 208*61d06d6bSBaptiste Daroussin return; 209*61d06d6bSBaptiste Daroussin } else if ( ! strcmp(p, "=")) { 210*61d06d6bSBaptiste Daroussin sp = newspan(tbl, ln, rp); 211*61d06d6bSBaptiste Daroussin sp->pos = TBL_SPAN_DHORIZ; 212*61d06d6bSBaptiste Daroussin return; 213*61d06d6bSBaptiste Daroussin } 214*61d06d6bSBaptiste Daroussin 215*61d06d6bSBaptiste Daroussin /* 216*61d06d6bSBaptiste Daroussin * If the layout row contains nothing but horizontal lines, 217*61d06d6bSBaptiste Daroussin * allocate an empty span for it and assign the current span 218*61d06d6bSBaptiste Daroussin * to the next layout row accepting data. 219*61d06d6bSBaptiste Daroussin */ 220*61d06d6bSBaptiste Daroussin 221*61d06d6bSBaptiste Daroussin while (rp->next != NULL) { 222*61d06d6bSBaptiste Daroussin if (rp->last->col + 1 < tbl->opts.cols) 223*61d06d6bSBaptiste Daroussin break; 224*61d06d6bSBaptiste Daroussin for (cp = rp->first; cp != NULL; cp = cp->next) 225*61d06d6bSBaptiste Daroussin if (cp->pos != TBL_CELL_HORIZ && 226*61d06d6bSBaptiste Daroussin cp->pos != TBL_CELL_DHORIZ) 227*61d06d6bSBaptiste Daroussin break; 228*61d06d6bSBaptiste Daroussin if (cp != NULL) 229*61d06d6bSBaptiste Daroussin break; 230*61d06d6bSBaptiste Daroussin sp = newspan(tbl, ln, rp); 231*61d06d6bSBaptiste Daroussin sp->pos = TBL_SPAN_DATA; 232*61d06d6bSBaptiste Daroussin rp = rp->next; 233*61d06d6bSBaptiste Daroussin } 234*61d06d6bSBaptiste Daroussin 235*61d06d6bSBaptiste Daroussin /* Process a real data row. */ 236*61d06d6bSBaptiste Daroussin 237*61d06d6bSBaptiste Daroussin sp = newspan(tbl, ln, rp); 238*61d06d6bSBaptiste Daroussin sp->pos = TBL_SPAN_DATA; 239*61d06d6bSBaptiste Daroussin while (p[pos] != '\0') 240*61d06d6bSBaptiste Daroussin getdata(tbl, sp, ln, p, &pos); 241*61d06d6bSBaptiste Daroussin } 242