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