xref: /openbsd-src/usr.bin/mandoc/tbl_data.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: tbl_data.c,v 1.28 2015/10/06 18:30:44 schwarze Exp $ */
2 /*
3  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2011, 2015 Ingo Schwarze <schwarze@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 <sys/types.h>
19 
20 #include <assert.h>
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 
26 #include "mandoc.h"
27 #include "mandoc_aux.h"
28 #include "libmandoc.h"
29 #include "libroff.h"
30 
31 static	void		 getdata(struct tbl_node *, struct tbl_span *,
32 				int, const char *, int *);
33 static	struct tbl_span	*newspan(struct tbl_node *, int,
34 				struct tbl_row *);
35 
36 
37 static void
38 getdata(struct tbl_node *tbl, struct tbl_span *dp,
39 		int ln, const char *p, int *pos)
40 {
41 	struct tbl_dat	*dat;
42 	struct tbl_cell	*cp;
43 	int		 sv;
44 
45 	/* Advance to the next layout cell, skipping spanners. */
46 
47 	cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next;
48 	while (cp != NULL && cp->pos == TBL_CELL_SPAN)
49 		cp = cp->next;
50 
51 	/*
52 	 * Stop processing when we reach the end of the available layout
53 	 * cells.  This means that we have extra input.
54 	 */
55 
56 	if (cp == NULL) {
57 		mandoc_msg(MANDOCERR_TBLDATA_EXTRA, tbl->parse,
58 		    ln, *pos, p + *pos);
59 		/* Skip to the end... */
60 		while (p[*pos])
61 			(*pos)++;
62 		return;
63 	}
64 
65 	dat = mandoc_calloc(1, sizeof(*dat));
66 	dat->layout = cp;
67 	dat->pos = TBL_DATA_NONE;
68 	dat->spans = 0;
69 	for (cp = cp->next; cp != NULL; cp = cp->next)
70 		if (cp->pos == TBL_CELL_SPAN)
71 			dat->spans++;
72 		else
73 			break;
74 
75 	if (dp->last == NULL)
76 		dp->first = dat;
77 	else
78 		dp->last->next = dat;
79 	dp->last = dat;
80 
81 	sv = *pos;
82 	while (p[*pos] && p[*pos] != tbl->opts.tab)
83 		(*pos)++;
84 
85 	/*
86 	 * Check for a continued-data scope opening.  This consists of a
87 	 * trailing `T{' at the end of the line.  Subsequent lines,
88 	 * until a standalone `T}', are included in our cell.
89 	 */
90 
91 	if (*pos - sv == 2 && p[sv] == 'T' && p[sv + 1] == '{') {
92 		tbl->part = TBL_PART_CDATA;
93 		return;
94 	}
95 
96 	dat->string = mandoc_strndup(p + sv, *pos - sv);
97 
98 	if (p[*pos])
99 		(*pos)++;
100 
101 	if ( ! strcmp(dat->string, "_"))
102 		dat->pos = TBL_DATA_HORIZ;
103 	else if ( ! strcmp(dat->string, "="))
104 		dat->pos = TBL_DATA_DHORIZ;
105 	else if ( ! strcmp(dat->string, "\\_"))
106 		dat->pos = TBL_DATA_NHORIZ;
107 	else if ( ! strcmp(dat->string, "\\="))
108 		dat->pos = TBL_DATA_NDHORIZ;
109 	else
110 		dat->pos = TBL_DATA_DATA;
111 
112 	if ((dat->layout->pos == TBL_CELL_HORIZ ||
113 	    dat->layout->pos == TBL_CELL_DHORIZ ||
114 	    dat->layout->pos == TBL_CELL_DOWN) &&
115 	    dat->pos == TBL_DATA_DATA && *dat->string != '\0')
116 		mandoc_msg(MANDOCERR_TBLDATA_SPAN,
117 		    tbl->parse, ln, sv, dat->string);
118 }
119 
120 int
121 tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos)
122 {
123 	struct tbl_dat	*dat;
124 	size_t		 sz;
125 
126 	dat = tbl->last_span->last;
127 
128 	if (p[pos] == 'T' && p[pos + 1] == '}') {
129 		pos += 2;
130 		if (p[pos] == tbl->opts.tab) {
131 			tbl->part = TBL_PART_DATA;
132 			pos++;
133 			while (p[pos] != '\0')
134 				getdata(tbl, tbl->last_span, ln, p, &pos);
135 			return 1;
136 		} else if (p[pos] == '\0') {
137 			tbl->part = TBL_PART_DATA;
138 			return 1;
139 		}
140 
141 		/* Fallthrough: T} is part of a word. */
142 	}
143 
144 	dat->pos = TBL_DATA_DATA;
145 
146 	if (dat->string != NULL) {
147 		sz = strlen(p + pos) + strlen(dat->string) + 2;
148 		dat->string = mandoc_realloc(dat->string, sz);
149 		(void)strlcat(dat->string, " ", sz);
150 		(void)strlcat(dat->string, p + pos, sz);
151 	} else
152 		dat->string = mandoc_strdup(p + pos);
153 
154 	if (dat->layout->pos == TBL_CELL_DOWN)
155 		mandoc_msg(MANDOCERR_TBLDATA_SPAN, tbl->parse,
156 		    ln, pos, dat->string);
157 
158 	return 0;
159 }
160 
161 static struct tbl_span *
162 newspan(struct tbl_node *tbl, int line, struct tbl_row *rp)
163 {
164 	struct tbl_span	*dp;
165 
166 	dp = mandoc_calloc(1, sizeof(*dp));
167 	dp->line = line;
168 	dp->opts = &tbl->opts;
169 	dp->layout = rp;
170 	dp->prev = tbl->last_span;
171 
172 	if (dp->prev == NULL) {
173 		tbl->first_span = dp;
174 		tbl->current_span = NULL;
175 	} else
176 		dp->prev->next = dp;
177 	tbl->last_span = dp;
178 
179 	return dp;
180 }
181 
182 void
183 tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos)
184 {
185 	struct tbl_span	*dp;
186 	struct tbl_row	*rp;
187 
188 	/*
189 	 * Choose a layout row: take the one following the last parsed
190 	 * span's.  If that doesn't exist, use the last parsed span's.
191 	 * If there's no last parsed span, use the first row.  Lastly,
192 	 * if the last span was a horizontal line, use the same layout
193 	 * (it doesn't "consume" the layout).
194 	 */
195 
196 	if (tbl->last_span != NULL) {
197 		if (tbl->last_span->pos == TBL_SPAN_DATA) {
198 			for (rp = tbl->last_span->layout->next;
199 			     rp != NULL && rp->first != NULL;
200 			     rp = rp->next) {
201 				switch (rp->first->pos) {
202 				case TBL_CELL_HORIZ:
203 					dp = newspan(tbl, ln, rp);
204 					dp->pos = TBL_SPAN_HORIZ;
205 					continue;
206 				case TBL_CELL_DHORIZ:
207 					dp = newspan(tbl, ln, rp);
208 					dp->pos = TBL_SPAN_DHORIZ;
209 					continue;
210 				default:
211 					break;
212 				}
213 				break;
214 			}
215 		} else
216 			rp = tbl->last_span->layout;
217 
218 		if (rp == NULL)
219 			rp = tbl->last_span->layout;
220 	} else
221 		rp = tbl->first_row;
222 
223 	assert(rp);
224 
225 	dp = newspan(tbl, ln, rp);
226 
227 	if ( ! strcmp(p, "_")) {
228 		dp->pos = TBL_SPAN_HORIZ;
229 		return;
230 	} else if ( ! strcmp(p, "=")) {
231 		dp->pos = TBL_SPAN_DHORIZ;
232 		return;
233 	}
234 
235 	dp->pos = TBL_SPAN_DATA;
236 
237 	while (p[pos] != '\0')
238 		getdata(tbl, dp, ln, p, &pos);
239 }
240