Home | History | Annotate | Line # | Download | only in dist
tbl_data.c revision 1.9
      1 /*	Id: tbl_data.c,v 1.52 2019/02/09 16:00:39 schwarze Exp  */
      2 /*
      3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps (at) bsd.lv>
      4  * Copyright (c) 2011,2015,2017,2018,2019 Ingo Schwarze <schwarze (at) openbsd.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 #include "config.h"
     19 
     20 #include <sys/types.h>
     21 
     22 #include <assert.h>
     23 #include <ctype.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <time.h>
     28 
     29 #include "mandoc_aux.h"
     30 #include "mandoc.h"
     31 #include "tbl.h"
     32 #include "libmandoc.h"
     33 #include "tbl_int.h"
     34 
     35 static	void		 getdata(struct tbl_node *, struct tbl_span *,
     36 				int, const char *, int *);
     37 static	struct tbl_span	*newspan(struct tbl_node *, int,
     38 				struct tbl_row *);
     39 
     40 
     41 static void
     42 getdata(struct tbl_node *tbl, struct tbl_span *dp,
     43 		int ln, const char *p, int *pos)
     44 {
     45 	struct tbl_dat	*dat, *pdat;
     46 	struct tbl_cell	*cp;
     47 	struct tbl_span	*pdp;
     48 	int		 sv;
     49 
     50 	/*
     51 	 * Determine the length of the string in the cell
     52 	 * and advance the parse point to the end of the cell.
     53 	 */
     54 
     55 	sv = *pos;
     56 	while (p[*pos] != '\0' && p[*pos] != tbl->opts.tab)
     57 		(*pos)++;
     58 
     59 	/* Advance to the next layout cell, skipping spanners. */
     60 
     61 	cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next;
     62 	while (cp != NULL && cp->pos == TBL_CELL_SPAN)
     63 		cp = cp->next;
     64 
     65 	/*
     66 	 * If the current layout row is out of cells, allocate
     67 	 * a new cell if another row of the table has at least
     68 	 * this number of columns, or discard the input if we
     69 	 * are beyond the last column of the table as a whole.
     70 	 */
     71 
     72 	if (cp == NULL) {
     73 		if (dp->layout->last->col + 1 < dp->opts->cols) {
     74 			cp = mandoc_calloc(1, sizeof(*cp));
     75 			cp->pos = TBL_CELL_LEFT;
     76 			dp->layout->last->next = cp;
     77 			cp->col = dp->layout->last->col + 1;
     78 			dp->layout->last = cp;
     79 		} else {
     80 			mandoc_msg(MANDOCERR_TBLDATA_EXTRA,
     81 			    ln, sv, "%s", p + sv);
     82 			while (p[*pos] != '\0')
     83 				(*pos)++;
     84 			return;
     85 		}
     86 	}
     87 
     88 	dat = mandoc_malloc(sizeof(*dat));
     89 	dat->layout = cp;
     90 	dat->next = NULL;
     91 	dat->string = NULL;
     92 	dat->hspans = 0;
     93 	dat->vspans = 0;
     94 	dat->block = 0;
     95 	dat->pos = TBL_DATA_NONE;
     96 
     97 	/*
     98 	 * Increment the number of vertical spans in a data cell above,
     99 	 * if this cell vertically extends one or more cells above.
    100 	 * The iteration must be done over data rows,
    101 	 * not over layout rows, because one layout row
    102 	 * can be reused for more than one data row.
    103 	 */
    104 
    105 	if (cp->pos == TBL_CELL_DOWN ||
    106 	    (*pos - sv == 2 && p[sv] == '\\' && p[sv + 1] == '^')) {
    107 		pdp = dp;
    108 		while ((pdp = pdp->prev) != NULL) {
    109 			pdat = pdp->first;
    110 			while (pdat != NULL &&
    111 			    pdat->layout->col < dat->layout->col)
    112 				pdat = pdat->next;
    113 			if (pdat == NULL)
    114 				break;
    115 			if (pdat->layout->pos != TBL_CELL_DOWN &&
    116 			    strcmp(pdat->string, "\\^") != 0) {
    117 				pdat->vspans++;
    118 				break;
    119 			}
    120 		}
    121 	}
    122 
    123 	/*
    124 	 * Count the number of horizontal spans to the right of this cell.
    125 	 * This is purely a matter of the layout, independent of the data.
    126 	 */
    127 
    128 	for (cp = cp->next; cp != NULL; cp = cp->next)
    129 		if (cp->pos == TBL_CELL_SPAN)
    130 			dat->hspans++;
    131 		else
    132 			break;
    133 
    134 	/* Append the new data cell to the data row. */
    135 
    136 	if (dp->last == NULL)
    137 		dp->first = dat;
    138 	else
    139 		dp->last->next = dat;
    140 	dp->last = dat;
    141 
    142 	/*
    143 	 * Check for a continued-data scope opening.  This consists of a
    144 	 * trailing `T{' at the end of the line.  Subsequent lines,
    145 	 * until a standalone `T}', are included in our cell.
    146 	 */
    147 
    148 	if (*pos - sv == 2 && p[sv] == 'T' && p[sv + 1] == '{') {
    149 		tbl->part = TBL_PART_CDATA;
    150 		return;
    151 	}
    152 
    153 	dat->string = mandoc_strndup(p + sv, *pos - sv);
    154 
    155 	if (p[*pos] != '\0')
    156 		(*pos)++;
    157 
    158 	if ( ! strcmp(dat->string, "_"))
    159 		dat->pos = TBL_DATA_HORIZ;
    160 	else if ( ! strcmp(dat->string, "="))
    161 		dat->pos = TBL_DATA_DHORIZ;
    162 	else if ( ! strcmp(dat->string, "\\_"))
    163 		dat->pos = TBL_DATA_NHORIZ;
    164 	else if ( ! strcmp(dat->string, "\\="))
    165 		dat->pos = TBL_DATA_NDHORIZ;
    166 	else
    167 		dat->pos = TBL_DATA_DATA;
    168 
    169 	if ((dat->layout->pos == TBL_CELL_HORIZ ||
    170 	    dat->layout->pos == TBL_CELL_DHORIZ ||
    171 	    dat->layout->pos == TBL_CELL_DOWN) &&
    172 	    dat->pos == TBL_DATA_DATA && *dat->string != '\0')
    173 		mandoc_msg(MANDOCERR_TBLDATA_SPAN,
    174 		    ln, sv, "%s", dat->string);
    175 }
    176 
    177 void
    178 tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos)
    179 {
    180 	struct tbl_dat	*dat;
    181 	size_t		 sz;
    182 
    183 	dat = tbl->last_span->last;
    184 
    185 	if (p[pos] == 'T' && p[pos + 1] == '}') {
    186 		pos += 2;
    187 		if (p[pos] == tbl->opts.tab) {
    188 			tbl->part = TBL_PART_DATA;
    189 			pos++;
    190 			while (p[pos] != '\0')
    191 				getdata(tbl, tbl->last_span, ln, p, &pos);
    192 			return;
    193 		} else if (p[pos] == '\0') {
    194 			tbl->part = TBL_PART_DATA;
    195 			return;
    196 		}
    197 
    198 		/* Fallthrough: T} is part of a word. */
    199 	}
    200 
    201 	dat->pos = TBL_DATA_DATA;
    202 	dat->block = 1;
    203 
    204 	if (dat->string != NULL) {
    205 		sz = strlen(p + pos) + strlen(dat->string) + 2;
    206 		dat->string = mandoc_realloc(dat->string, sz);
    207 		(void)strlcat(dat->string, " ", sz);
    208 		(void)strlcat(dat->string, p + pos, sz);
    209 	} else
    210 		dat->string = mandoc_strdup(p + pos);
    211 
    212 	if (dat->layout->pos == TBL_CELL_DOWN)
    213 		mandoc_msg(MANDOCERR_TBLDATA_SPAN,
    214 		    ln, pos, "%s", dat->string);
    215 }
    216 
    217 static struct tbl_span *
    218 newspan(struct tbl_node *tbl, int line, struct tbl_row *rp)
    219 {
    220 	struct tbl_span	*dp;
    221 
    222 	dp = mandoc_calloc(1, sizeof(*dp));
    223 	dp->line = line;
    224 	dp->opts = &tbl->opts;
    225 	dp->layout = rp;
    226 	dp->prev = tbl->last_span;
    227 
    228 	if (dp->prev == NULL) {
    229 		tbl->first_span = dp;
    230 		tbl->current_span = NULL;
    231 	} else
    232 		dp->prev->next = dp;
    233 	tbl->last_span = dp;
    234 
    235 	return dp;
    236 }
    237 
    238 void
    239 tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos)
    240 {
    241 	struct tbl_row	*rp;
    242 	struct tbl_cell	*cp;
    243 	struct tbl_span	*sp;
    244 
    245 	rp = (sp = tbl->last_span) == NULL ? tbl->first_row :
    246 	    sp->pos == TBL_SPAN_DATA && sp->layout->next != NULL ?
    247 	    sp->layout->next : sp->layout;
    248 
    249 	assert(rp != NULL);
    250 
    251 	if (p[1] == '\0') {
    252 		switch (p[0]) {
    253 		case '.':
    254 			/*
    255 			 * Empty request lines must be handled here
    256 			 * and cannot be discarded in roff_parseln()
    257 			 * because in the layout section, they
    258 			 * are significant and end the layout.
    259 			 */
    260 			return;
    261 		case '_':
    262 			sp = newspan(tbl, ln, rp);
    263 			sp->pos = TBL_SPAN_HORIZ;
    264 			return;
    265 		case '=':
    266 			sp = newspan(tbl, ln, rp);
    267 			sp->pos = TBL_SPAN_DHORIZ;
    268 			return;
    269 		default:
    270 			break;
    271 		}
    272 	}
    273 
    274 	/*
    275 	 * If the layout row contains nothing but horizontal lines,
    276 	 * allocate an empty span for it and assign the current span
    277 	 * to the next layout row accepting data.
    278 	 */
    279 
    280 	while (rp->next != NULL) {
    281 		if (rp->last->col + 1 < tbl->opts.cols)
    282 			break;
    283 		for (cp = rp->first; cp != NULL; cp = cp->next)
    284 			if (cp->pos != TBL_CELL_HORIZ &&
    285 			    cp->pos != TBL_CELL_DHORIZ)
    286 				break;
    287 		if (cp != NULL)
    288 			break;
    289 		sp = newspan(tbl, ln, rp);
    290 		sp->pos = TBL_SPAN_DATA;
    291 		rp = rp->next;
    292 	}
    293 
    294 	/* Process a real data row. */
    295 
    296 	sp = newspan(tbl, ln, rp);
    297 	sp->pos = TBL_SPAN_DATA;
    298 	while (p[pos] != '\0')
    299 		getdata(tbl, sp, ln, p, &pos);
    300 }
    301