Home | History | Annotate | Line # | Download | only in dist
tbl_data.c revision 1.9
      1  1.9  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.9  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.9  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.9  christos #include "mandoc_aux.h"
     30  1.1     joerg #include "mandoc.h"
     31  1.9  christos #include "tbl.h"
     32  1.1     joerg #include "libmandoc.h"
     33  1.9  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.9  christos 	struct tbl_dat	*dat, *pdat;
     46  1.1     joerg 	struct tbl_cell	*cp;
     47  1.9  christos 	struct tbl_span	*pdp;
     48  1.6  christos 	int		 sv;
     49  1.1     joerg 
     50  1.9  christos 	/*
     51  1.9  christos 	 * Determine the length of the string in the cell
     52  1.9  christos 	 * and advance the parse point to the end of the cell.
     53  1.9  christos 	 */
     54  1.9  christos 
     55  1.9  christos 	sv = *pos;
     56  1.9  christos 	while (p[*pos] != '\0' && p[*pos] != tbl->opts.tab)
     57  1.9  christos 		(*pos)++;
     58  1.9  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.8  christos 	 * If the current layout row is out of cells, allocate
     67  1.8  christos 	 * a new cell if another row of the table has at least
     68  1.8  christos 	 * this number of columns, or discard the input if we
     69  1.8  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.8  christos 		if (dp->layout->last->col + 1 < dp->opts->cols) {
     74  1.8  christos 			cp = mandoc_calloc(1, sizeof(*cp));
     75  1.8  christos 			cp->pos = TBL_CELL_LEFT;
     76  1.8  christos 			dp->layout->last->next = cp;
     77  1.8  christos 			cp->col = dp->layout->last->col + 1;
     78  1.8  christos 			dp->layout->last = cp;
     79  1.8  christos 		} else {
     80  1.9  christos 			mandoc_msg(MANDOCERR_TBLDATA_EXTRA,
     81  1.9  christos 			    ln, sv, "%s", p + sv);
     82  1.9  christos 			while (p[*pos] != '\0')
     83  1.8  christos 				(*pos)++;
     84  1.8  christos 			return;
     85  1.8  christos 		}
     86  1.2  christos 	}
     87  1.2  christos 
     88  1.9  christos 	dat = mandoc_malloc(sizeof(*dat));
     89  1.1     joerg 	dat->layout = cp;
     90  1.9  christos 	dat->next = NULL;
     91  1.9  christos 	dat->string = NULL;
     92  1.9  christos 	dat->hspans = 0;
     93  1.9  christos 	dat->vspans = 0;
     94  1.9  christos 	dat->block = 0;
     95  1.1     joerg 	dat->pos = TBL_DATA_NONE;
     96  1.9  christos 
     97  1.9  christos 	/*
     98  1.9  christos 	 * Increment the number of vertical spans in a data cell above,
     99  1.9  christos 	 * if this cell vertically extends one or more cells above.
    100  1.9  christos 	 * The iteration must be done over data rows,
    101  1.9  christos 	 * not over layout rows, because one layout row
    102  1.9  christos 	 * can be reused for more than one data row.
    103  1.9  christos 	 */
    104  1.9  christos 
    105  1.9  christos 	if (cp->pos == TBL_CELL_DOWN ||
    106  1.9  christos 	    (*pos - sv == 2 && p[sv] == '\\' && p[sv + 1] == '^')) {
    107  1.9  christos 		pdp = dp;
    108  1.9  christos 		while ((pdp = pdp->prev) != NULL) {
    109  1.9  christos 			pdat = pdp->first;
    110  1.9  christos 			while (pdat != NULL &&
    111  1.9  christos 			    pdat->layout->col < dat->layout->col)
    112  1.9  christos 				pdat = pdat->next;
    113  1.9  christos 			if (pdat == NULL)
    114  1.9  christos 				break;
    115  1.9  christos 			if (pdat->layout->pos != TBL_CELL_DOWN &&
    116  1.9  christos 			    strcmp(pdat->string, "\\^") != 0) {
    117  1.9  christos 				pdat->vspans++;
    118  1.9  christos 				break;
    119  1.9  christos 			}
    120  1.9  christos 		}
    121  1.9  christos 	}
    122  1.9  christos 
    123  1.9  christos 	/*
    124  1.9  christos 	 * Count the number of horizontal spans to the right of this cell.
    125  1.9  christos 	 * This is purely a matter of the layout, independent of the data.
    126  1.9  christos 	 */
    127  1.9  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.9  christos 			dat->hspans++;
    131  1.2  christos 		else
    132  1.2  christos 			break;
    133  1.1     joerg 
    134  1.9  christos 	/* Append the new data cell to the data row. */
    135  1.9  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.9  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.9  christos 		    ln, sv, "%s", dat->string);
    175  1.1     joerg }
    176  1.1     joerg 
    177  1.8  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.8  christos 			return;
    193  1.6  christos 		} else if (p[pos] == '\0') {
    194  1.1     joerg 			tbl->part = TBL_PART_DATA;
    195  1.8  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.8  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.9  christos 		mandoc_msg(MANDOCERR_TBLDATA_SPAN,
    214  1.9  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.8  christos 	struct tbl_cell	*cp;
    243  1.8  christos 	struct tbl_span	*sp;
    244  1.1     joerg 
    245  1.8  christos 	rp = (sp = tbl->last_span) == NULL ? tbl->first_row :
    246  1.8  christos 	    sp->pos == TBL_SPAN_DATA && sp->layout->next != NULL ?
    247  1.8  christos 	    sp->layout->next : sp->layout;
    248  1.1     joerg 
    249  1.8  christos 	assert(rp != NULL);
    250  1.1     joerg 
    251  1.9  christos 	if (p[1] == '\0') {
    252  1.9  christos 		switch (p[0]) {
    253  1.9  christos 		case '.':
    254  1.9  christos 			/*
    255  1.9  christos 			 * Empty request lines must be handled here
    256  1.9  christos 			 * and cannot be discarded in roff_parseln()
    257  1.9  christos 			 * because in the layout section, they
    258  1.9  christos 			 * are significant and end the layout.
    259  1.9  christos 			 */
    260  1.9  christos 			return;
    261  1.9  christos 		case '_':
    262  1.9  christos 			sp = newspan(tbl, ln, rp);
    263  1.9  christos 			sp->pos = TBL_SPAN_HORIZ;
    264  1.9  christos 			return;
    265  1.9  christos 		case '=':
    266  1.9  christos 			sp = newspan(tbl, ln, rp);
    267  1.9  christos 			sp->pos = TBL_SPAN_DHORIZ;
    268  1.9  christos 			return;
    269  1.9  christos 		default:
    270  1.9  christos 			break;
    271  1.9  christos 		}
    272  1.1     joerg 	}
    273  1.1     joerg 
    274  1.8  christos 	/*
    275  1.8  christos 	 * If the layout row contains nothing but horizontal lines,
    276  1.8  christos 	 * allocate an empty span for it and assign the current span
    277  1.8  christos 	 * to the next layout row accepting data.
    278  1.8  christos 	 */
    279  1.8  christos 
    280  1.8  christos 	while (rp->next != NULL) {
    281  1.8  christos 		if (rp->last->col + 1 < tbl->opts.cols)
    282  1.8  christos 			break;
    283  1.8  christos 		for (cp = rp->first; cp != NULL; cp = cp->next)
    284  1.8  christos 			if (cp->pos != TBL_CELL_HORIZ &&
    285  1.8  christos 			    cp->pos != TBL_CELL_DHORIZ)
    286  1.8  christos 				break;
    287  1.8  christos 		if (cp != NULL)
    288  1.8  christos 			break;
    289  1.8  christos 		sp = newspan(tbl, ln, rp);
    290  1.8  christos 		sp->pos = TBL_SPAN_DATA;
    291  1.8  christos 		rp = rp->next;
    292  1.8  christos 	}
    293  1.8  christos 
    294  1.8  christos 	/* Process a real data row. */
    295  1.1     joerg 
    296  1.8  christos 	sp = newspan(tbl, ln, rp);
    297  1.8  christos 	sp->pos = TBL_SPAN_DATA;
    298  1.6  christos 	while (p[pos] != '\0')
    299  1.8  christos 		getdata(tbl, sp, ln, p, &pos);
    300  1.1     joerg }
    301