xref: /freebsd/contrib/mandoc/tbl_data.c (revision 61d06d6bd19dafe8ea971dd43e8328fa1b473456)
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