tbl_data.c revision 1.10 1 1.10 wiz /* Id: tbl_data.c,v 1.59 2021/09/10 13:24:38 schwarze Exp */
2 1.1 joerg /*
3 1.2 christos * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps (at) bsd.lv>
4 1.10 wiz * Copyright (c) 2011,2015,2017-2019,2021 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.10 wiz #include <stdint.h>
25 1.9 christos #include <stdio.h>
26 1.1 joerg #include <stdlib.h>
27 1.1 joerg #include <string.h>
28 1.1 joerg #include <time.h>
29 1.1 joerg
30 1.9 christos #include "mandoc_aux.h"
31 1.1 joerg #include "mandoc.h"
32 1.9 christos #include "tbl.h"
33 1.1 joerg #include "libmandoc.h"
34 1.9 christos #include "tbl_int.h"
35 1.1 joerg
36 1.6 christos static void getdata(struct tbl_node *, struct tbl_span *,
37 1.2 christos int, const char *, int *);
38 1.6 christos static struct tbl_span *newspan(struct tbl_node *, int,
39 1.2 christos struct tbl_row *);
40 1.1 joerg
41 1.6 christos
42 1.6 christos static void
43 1.6 christos getdata(struct tbl_node *tbl, struct tbl_span *dp,
44 1.1 joerg int ln, const char *p, int *pos)
45 1.1 joerg {
46 1.9 christos struct tbl_dat *dat, *pdat;
47 1.1 joerg struct tbl_cell *cp;
48 1.9 christos struct tbl_span *pdp;
49 1.10 wiz const char *ccp;
50 1.10 wiz int startpos, endpos;
51 1.1 joerg
52 1.9 christos /*
53 1.9 christos * Determine the length of the string in the cell
54 1.9 christos * and advance the parse point to the end of the cell.
55 1.9 christos */
56 1.9 christos
57 1.10 wiz startpos = *pos;
58 1.10 wiz ccp = p + startpos;
59 1.10 wiz while (*ccp != '\0' && *ccp != tbl->opts.tab)
60 1.10 wiz if (*ccp++ == '\\')
61 1.10 wiz mandoc_escape(&ccp, NULL, NULL);
62 1.10 wiz *pos = ccp - p;
63 1.9 christos
64 1.6 christos /* Advance to the next layout cell, skipping spanners. */
65 1.1 joerg
66 1.6 christos cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next;
67 1.6 christos while (cp != NULL && cp->pos == TBL_CELL_SPAN)
68 1.1 joerg cp = cp->next;
69 1.1 joerg
70 1.2 christos /*
71 1.8 christos * If the current layout row is out of cells, allocate
72 1.8 christos * a new cell if another row of the table has at least
73 1.8 christos * this number of columns, or discard the input if we
74 1.8 christos * are beyond the last column of the table as a whole.
75 1.2 christos */
76 1.2 christos
77 1.6 christos if (cp == NULL) {
78 1.8 christos if (dp->layout->last->col + 1 < dp->opts->cols) {
79 1.8 christos cp = mandoc_calloc(1, sizeof(*cp));
80 1.8 christos cp->pos = TBL_CELL_LEFT;
81 1.10 wiz cp->font = ESCAPE_FONTROMAN;
82 1.10 wiz cp->spacing = SIZE_MAX;
83 1.8 christos dp->layout->last->next = cp;
84 1.8 christos cp->col = dp->layout->last->col + 1;
85 1.8 christos dp->layout->last = cp;
86 1.8 christos } else {
87 1.9 christos mandoc_msg(MANDOCERR_TBLDATA_EXTRA,
88 1.10 wiz ln, startpos, "%s", p + startpos);
89 1.9 christos while (p[*pos] != '\0')
90 1.8 christos (*pos)++;
91 1.8 christos return;
92 1.8 christos }
93 1.2 christos }
94 1.2 christos
95 1.9 christos dat = mandoc_malloc(sizeof(*dat));
96 1.1 joerg dat->layout = cp;
97 1.9 christos dat->next = NULL;
98 1.9 christos dat->string = NULL;
99 1.9 christos dat->hspans = 0;
100 1.9 christos dat->vspans = 0;
101 1.9 christos dat->block = 0;
102 1.1 joerg dat->pos = TBL_DATA_NONE;
103 1.9 christos
104 1.9 christos /*
105 1.9 christos * Increment the number of vertical spans in a data cell above,
106 1.9 christos * if this cell vertically extends one or more cells above.
107 1.9 christos * The iteration must be done over data rows,
108 1.9 christos * not over layout rows, because one layout row
109 1.9 christos * can be reused for more than one data row.
110 1.9 christos */
111 1.9 christos
112 1.9 christos if (cp->pos == TBL_CELL_DOWN ||
113 1.10 wiz (*pos - startpos == 2 &&
114 1.10 wiz p[startpos] == '\\' && p[startpos + 1] == '^')) {
115 1.9 christos pdp = dp;
116 1.9 christos while ((pdp = pdp->prev) != NULL) {
117 1.9 christos pdat = pdp->first;
118 1.9 christos while (pdat != NULL &&
119 1.9 christos pdat->layout->col < dat->layout->col)
120 1.9 christos pdat = pdat->next;
121 1.9 christos if (pdat == NULL)
122 1.9 christos break;
123 1.9 christos if (pdat->layout->pos != TBL_CELL_DOWN &&
124 1.9 christos strcmp(pdat->string, "\\^") != 0) {
125 1.9 christos pdat->vspans++;
126 1.9 christos break;
127 1.9 christos }
128 1.9 christos }
129 1.9 christos }
130 1.9 christos
131 1.9 christos /*
132 1.9 christos * Count the number of horizontal spans to the right of this cell.
133 1.9 christos * This is purely a matter of the layout, independent of the data.
134 1.9 christos */
135 1.9 christos
136 1.6 christos for (cp = cp->next; cp != NULL; cp = cp->next)
137 1.6 christos if (cp->pos == TBL_CELL_SPAN)
138 1.9 christos dat->hspans++;
139 1.2 christos else
140 1.2 christos break;
141 1.1 joerg
142 1.9 christos /* Append the new data cell to the data row. */
143 1.9 christos
144 1.6 christos if (dp->last == NULL)
145 1.6 christos dp->first = dat;
146 1.6 christos else
147 1.1 joerg dp->last->next = dat;
148 1.6 christos dp->last = dat;
149 1.1 joerg
150 1.10 wiz /* Strip leading and trailing spaces, if requested. */
151 1.10 wiz
152 1.10 wiz endpos = *pos;
153 1.10 wiz if (dp->opts->opts & TBL_OPT_NOSPACE) {
154 1.10 wiz while (p[startpos] == ' ')
155 1.10 wiz startpos++;
156 1.10 wiz while (endpos > startpos && p[endpos - 1] == ' ')
157 1.10 wiz endpos--;
158 1.10 wiz }
159 1.10 wiz
160 1.1 joerg /*
161 1.1 joerg * Check for a continued-data scope opening. This consists of a
162 1.1 joerg * trailing `T{' at the end of the line. Subsequent lines,
163 1.1 joerg * until a standalone `T}', are included in our cell.
164 1.1 joerg */
165 1.1 joerg
166 1.10 wiz if (endpos - startpos == 2 &&
167 1.10 wiz p[startpos] == 'T' && p[startpos + 1] == '{') {
168 1.1 joerg tbl->part = TBL_PART_CDATA;
169 1.6 christos return;
170 1.1 joerg }
171 1.1 joerg
172 1.10 wiz dat->string = mandoc_strndup(p + startpos, endpos - startpos);
173 1.1 joerg
174 1.9 christos if (p[*pos] != '\0')
175 1.1 joerg (*pos)++;
176 1.1 joerg
177 1.1 joerg if ( ! strcmp(dat->string, "_"))
178 1.1 joerg dat->pos = TBL_DATA_HORIZ;
179 1.1 joerg else if ( ! strcmp(dat->string, "="))
180 1.1 joerg dat->pos = TBL_DATA_DHORIZ;
181 1.1 joerg else if ( ! strcmp(dat->string, "\\_"))
182 1.1 joerg dat->pos = TBL_DATA_NHORIZ;
183 1.1 joerg else if ( ! strcmp(dat->string, "\\="))
184 1.1 joerg dat->pos = TBL_DATA_NDHORIZ;
185 1.1 joerg else
186 1.1 joerg dat->pos = TBL_DATA_DATA;
187 1.1 joerg
188 1.6 christos if ((dat->layout->pos == TBL_CELL_HORIZ ||
189 1.6 christos dat->layout->pos == TBL_CELL_DHORIZ ||
190 1.6 christos dat->layout->pos == TBL_CELL_DOWN) &&
191 1.6 christos dat->pos == TBL_DATA_DATA && *dat->string != '\0')
192 1.6 christos mandoc_msg(MANDOCERR_TBLDATA_SPAN,
193 1.10 wiz ln, startpos, "%s", dat->string);
194 1.1 joerg }
195 1.1 joerg
196 1.8 christos void
197 1.6 christos tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos)
198 1.1 joerg {
199 1.1 joerg struct tbl_dat *dat;
200 1.6 christos size_t sz;
201 1.1 joerg
202 1.1 joerg dat = tbl->last_span->last;
203 1.1 joerg
204 1.1 joerg if (p[pos] == 'T' && p[pos + 1] == '}') {
205 1.1 joerg pos += 2;
206 1.10 wiz if (tbl->opts.opts & TBL_OPT_NOSPACE)
207 1.10 wiz while (p[pos] == ' ')
208 1.10 wiz pos++;
209 1.1 joerg if (p[pos] == tbl->opts.tab) {
210 1.1 joerg tbl->part = TBL_PART_DATA;
211 1.1 joerg pos++;
212 1.7 christos while (p[pos] != '\0')
213 1.7 christos getdata(tbl, tbl->last_span, ln, p, &pos);
214 1.8 christos return;
215 1.6 christos } else if (p[pos] == '\0') {
216 1.1 joerg tbl->part = TBL_PART_DATA;
217 1.8 christos return;
218 1.1 joerg }
219 1.1 joerg
220 1.1 joerg /* Fallthrough: T} is part of a word. */
221 1.1 joerg }
222 1.1 joerg
223 1.2 christos dat->pos = TBL_DATA_DATA;
224 1.8 christos dat->block = 1;
225 1.2 christos
226 1.6 christos if (dat->string != NULL) {
227 1.6 christos sz = strlen(p + pos) + strlen(dat->string) + 2;
228 1.1 joerg dat->string = mandoc_realloc(dat->string, sz);
229 1.6 christos (void)strlcat(dat->string, " ", sz);
230 1.6 christos (void)strlcat(dat->string, p + pos, sz);
231 1.1 joerg } else
232 1.6 christos dat->string = mandoc_strdup(p + pos);
233 1.1 joerg
234 1.6 christos if (dat->layout->pos == TBL_CELL_DOWN)
235 1.9 christos mandoc_msg(MANDOCERR_TBLDATA_SPAN,
236 1.9 christos ln, pos, "%s", dat->string);
237 1.1 joerg }
238 1.1 joerg
239 1.2 christos static struct tbl_span *
240 1.2 christos newspan(struct tbl_node *tbl, int line, struct tbl_row *rp)
241 1.2 christos {
242 1.2 christos struct tbl_span *dp;
243 1.2 christos
244 1.6 christos dp = mandoc_calloc(1, sizeof(*dp));
245 1.2 christos dp->line = line;
246 1.5 joerg dp->opts = &tbl->opts;
247 1.2 christos dp->layout = rp;
248 1.6 christos dp->prev = tbl->last_span;
249 1.2 christos
250 1.6 christos if (dp->prev == NULL) {
251 1.6 christos tbl->first_span = dp;
252 1.2 christos tbl->current_span = NULL;
253 1.6 christos } else
254 1.6 christos dp->prev->next = dp;
255 1.6 christos tbl->last_span = dp;
256 1.2 christos
257 1.7 christos return dp;
258 1.2 christos }
259 1.2 christos
260 1.6 christos void
261 1.6 christos tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos)
262 1.1 joerg {
263 1.1 joerg struct tbl_row *rp;
264 1.8 christos struct tbl_cell *cp;
265 1.8 christos struct tbl_span *sp;
266 1.1 joerg
267 1.10 wiz for (sp = tbl->last_span; sp != NULL; sp = sp->prev)
268 1.10 wiz if (sp->pos == TBL_SPAN_DATA)
269 1.10 wiz break;
270 1.10 wiz rp = sp == NULL ? tbl->first_row :
271 1.10 wiz sp->layout->next == NULL ? sp->layout : sp->layout->next;
272 1.8 christos assert(rp != NULL);
273 1.1 joerg
274 1.9 christos if (p[1] == '\0') {
275 1.9 christos switch (p[0]) {
276 1.9 christos case '.':
277 1.9 christos /*
278 1.9 christos * Empty request lines must be handled here
279 1.9 christos * and cannot be discarded in roff_parseln()
280 1.9 christos * because in the layout section, they
281 1.9 christos * are significant and end the layout.
282 1.9 christos */
283 1.9 christos return;
284 1.9 christos case '_':
285 1.9 christos sp = newspan(tbl, ln, rp);
286 1.9 christos sp->pos = TBL_SPAN_HORIZ;
287 1.9 christos return;
288 1.9 christos case '=':
289 1.9 christos sp = newspan(tbl, ln, rp);
290 1.9 christos sp->pos = TBL_SPAN_DHORIZ;
291 1.9 christos return;
292 1.9 christos default:
293 1.9 christos break;
294 1.9 christos }
295 1.1 joerg }
296 1.1 joerg
297 1.8 christos /*
298 1.8 christos * If the layout row contains nothing but horizontal lines,
299 1.8 christos * allocate an empty span for it and assign the current span
300 1.8 christos * to the next layout row accepting data.
301 1.8 christos */
302 1.8 christos
303 1.8 christos while (rp->next != NULL) {
304 1.8 christos if (rp->last->col + 1 < tbl->opts.cols)
305 1.8 christos break;
306 1.8 christos for (cp = rp->first; cp != NULL; cp = cp->next)
307 1.8 christos if (cp->pos != TBL_CELL_HORIZ &&
308 1.8 christos cp->pos != TBL_CELL_DHORIZ)
309 1.8 christos break;
310 1.8 christos if (cp != NULL)
311 1.8 christos break;
312 1.8 christos sp = newspan(tbl, ln, rp);
313 1.8 christos sp->pos = TBL_SPAN_DATA;
314 1.8 christos rp = rp->next;
315 1.8 christos }
316 1.8 christos
317 1.8 christos /* Process a real data row. */
318 1.1 joerg
319 1.8 christos sp = newspan(tbl, ln, rp);
320 1.8 christos sp->pos = TBL_SPAN_DATA;
321 1.6 christos while (p[pos] != '\0')
322 1.8 christos getdata(tbl, sp, ln, p, &pos);
323 1.1 joerg }
324