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