1 |
|
|
/* $OpenBSD: tbl_data.c,v 1.32 2017/07/08 17:52:42 schwarze Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
4 |
|
|
* Copyright (c) 2011, 2015, 2017 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 |
✓✓ |
205695 |
cp = dp->last == NULL ? dp->layout->first : dp->last->layout->next; |
48 |
✓✓✓✓
|
274632 |
while (cp != NULL && cp->pos == TBL_CELL_SPAN) |
49 |
|
102 |
cp = cp->next; |
50 |
|
|
|
51 |
|
|
/* |
52 |
|
|
* If the current layout row is out of cells, allocate |
53 |
|
|
* a new cell if another row of the table has at least |
54 |
|
|
* this number of columns, or discard the input if we |
55 |
|
|
* are beyond the last column of the table as a whole. |
56 |
|
|
*/ |
57 |
|
|
|
58 |
✓✓ |
68565 |
if (cp == NULL) { |
59 |
✓✓ |
36 |
if (dp->layout->last->col + 1 < dp->opts->cols) { |
60 |
|
18 |
cp = mandoc_calloc(1, sizeof(*cp)); |
61 |
|
18 |
cp->pos = TBL_CELL_LEFT; |
62 |
|
18 |
dp->layout->last->next = cp; |
63 |
|
18 |
cp->col = dp->layout->last->col + 1; |
64 |
|
18 |
dp->layout->last = cp; |
65 |
|
|
} else { |
66 |
|
36 |
mandoc_msg(MANDOCERR_TBLDATA_EXTRA, tbl->parse, |
67 |
|
18 |
ln, *pos, p + *pos); |
68 |
✓✓ |
216 |
while (p[*pos]) |
69 |
|
90 |
(*pos)++; |
70 |
|
18 |
return; |
71 |
|
|
} |
72 |
|
18 |
} |
73 |
|
|
|
74 |
|
68547 |
dat = mandoc_calloc(1, sizeof(*dat)); |
75 |
|
68547 |
dat->layout = cp; |
76 |
|
68547 |
dat->pos = TBL_DATA_NONE; |
77 |
|
68547 |
dat->spans = 0; |
78 |
✓✓ |
137490 |
for (cp = cp->next; cp != NULL; cp = cp->next) |
79 |
✓✓ |
44086 |
if (cp->pos == TBL_CELL_SPAN) |
80 |
|
198 |
dat->spans++; |
81 |
|
|
else |
82 |
|
|
break; |
83 |
|
|
|
84 |
✓✓ |
68547 |
if (dp->last == NULL) |
85 |
|
26159 |
dp->first = dat; |
86 |
|
|
else |
87 |
|
42388 |
dp->last->next = dat; |
88 |
|
68547 |
dp->last = dat; |
89 |
|
|
|
90 |
|
68547 |
sv = *pos; |
91 |
✓✓✓✓
|
1403072 |
while (p[*pos] && p[*pos] != tbl->opts.tab) |
92 |
|
288427 |
(*pos)++; |
93 |
|
|
|
94 |
|
|
/* |
95 |
|
|
* Check for a continued-data scope opening. This consists of a |
96 |
|
|
* trailing `T{' at the end of the line. Subsequent lines, |
97 |
|
|
* until a standalone `T}', are included in our cell. |
98 |
|
|
*/ |
99 |
|
|
|
100 |
✓✓✓✓ ✓✓ |
115359 |
if (*pos - sv == 2 && p[sv] == 'T' && p[sv + 1] == '{') { |
101 |
|
1728 |
tbl->part = TBL_PART_CDATA; |
102 |
|
1728 |
return; |
103 |
|
|
} |
104 |
|
|
|
105 |
|
66819 |
dat->string = mandoc_strndup(p + sv, *pos - sv); |
106 |
|
|
|
107 |
✓✓ |
66819 |
if (p[*pos]) |
108 |
|
43723 |
(*pos)++; |
109 |
|
|
|
110 |
✓✓ |
66819 |
if ( ! strcmp(dat->string, "_")) |
111 |
|
3 |
dat->pos = TBL_DATA_HORIZ; |
112 |
✓✓ |
66816 |
else if ( ! strcmp(dat->string, "=")) |
113 |
|
3 |
dat->pos = TBL_DATA_DHORIZ; |
114 |
✓✓ |
66813 |
else if ( ! strcmp(dat->string, "\\_")) |
115 |
|
3 |
dat->pos = TBL_DATA_NHORIZ; |
116 |
|
66810 |
else if ( ! strcmp(dat->string, "\\=")) |
117 |
|
|
dat->pos = TBL_DATA_NDHORIZ; |
118 |
|
|
else |
119 |
|
|
dat->pos = TBL_DATA_DATA; |
120 |
|
|
|
121 |
✓✓✓✓
|
70989 |
if ((dat->layout->pos == TBL_CELL_HORIZ || |
122 |
✓✗ |
62706 |
dat->layout->pos == TBL_CELL_DHORIZ || |
123 |
✓✓ |
62706 |
dat->layout->pos == TBL_CELL_DOWN) && |
124 |
✓✗ |
8340 |
dat->pos == TBL_DATA_DATA && *dat->string != '\0') |
125 |
|
72 |
mandoc_msg(MANDOCERR_TBLDATA_SPAN, |
126 |
|
72 |
tbl->parse, ln, sv, dat->string); |
127 |
|
135384 |
} |
128 |
|
|
|
129 |
|
|
void |
130 |
|
|
tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos) |
131 |
|
|
{ |
132 |
|
|
struct tbl_dat *dat; |
133 |
|
|
size_t sz; |
134 |
|
|
|
135 |
|
3420 |
dat = tbl->last_span->last; |
136 |
|
|
|
137 |
✓✓✓✓
|
5151 |
if (p[pos] == 'T' && p[pos + 1] == '}') { |
138 |
|
1692 |
pos += 2; |
139 |
✓✓ |
1692 |
if (p[pos] == tbl->opts.tab) { |
140 |
|
87 |
tbl->part = TBL_PART_DATA; |
141 |
|
87 |
pos++; |
142 |
✓✓ |
390 |
while (p[pos] != '\0') |
143 |
|
108 |
getdata(tbl, tbl->last_span, ln, p, &pos); |
144 |
|
87 |
return; |
145 |
✓✗ |
1605 |
} else if (p[pos] == '\0') { |
146 |
|
1605 |
tbl->part = TBL_PART_DATA; |
147 |
|
1605 |
return; |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
/* Fallthrough: T} is part of a word. */ |
151 |
|
|
} |
152 |
|
|
|
153 |
|
1728 |
dat->pos = TBL_DATA_DATA; |
154 |
|
1728 |
dat->block = 1; |
155 |
|
|
|
156 |
✗✓ |
1728 |
if (dat->string != NULL) { |
157 |
|
|
sz = strlen(p + pos) + strlen(dat->string) + 2; |
158 |
|
|
dat->string = mandoc_realloc(dat->string, sz); |
159 |
|
|
(void)strlcat(dat->string, " ", sz); |
160 |
|
|
(void)strlcat(dat->string, p + pos, sz); |
161 |
|
|
} else |
162 |
|
1728 |
dat->string = mandoc_strdup(p + pos); |
163 |
|
|
|
164 |
✗✓ |
1728 |
if (dat->layout->pos == TBL_CELL_DOWN) |
165 |
|
|
mandoc_msg(MANDOCERR_TBLDATA_SPAN, tbl->parse, |
166 |
|
|
ln, pos, dat->string); |
167 |
|
5148 |
} |
168 |
|
|
|
169 |
|
|
static struct tbl_span * |
170 |
|
|
newspan(struct tbl_node *tbl, int line, struct tbl_row *rp) |
171 |
|
|
{ |
172 |
|
|
struct tbl_span *dp; |
173 |
|
|
|
174 |
|
53158 |
dp = mandoc_calloc(1, sizeof(*dp)); |
175 |
|
26579 |
dp->line = line; |
176 |
|
26579 |
dp->opts = &tbl->opts; |
177 |
|
26579 |
dp->layout = rp; |
178 |
|
26579 |
dp->prev = tbl->last_span; |
179 |
|
|
|
180 |
✓✓ |
26579 |
if (dp->prev == NULL) { |
181 |
|
6901 |
tbl->first_span = dp; |
182 |
|
6901 |
tbl->current_span = NULL; |
183 |
|
6901 |
} else |
184 |
|
19678 |
dp->prev->next = dp; |
185 |
|
26579 |
tbl->last_span = dp; |
186 |
|
|
|
187 |
|
26579 |
return dp; |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
void |
191 |
|
|
tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos) |
192 |
|
|
{ |
193 |
|
|
struct tbl_row *rp; |
194 |
|
|
struct tbl_cell *cp; |
195 |
|
|
struct tbl_span *sp; |
196 |
|
|
|
197 |
✓✓ |
33438 |
rp = (sp = tbl->last_span) == NULL ? tbl->first_row : |
198 |
✓✓✓✓
|
58593 |
sp->pos == TBL_SPAN_DATA && sp->layout->next != NULL ? |
199 |
|
5994 |
sp->layout->next : sp->layout; |
200 |
|
|
|
201 |
✗✓ |
26537 |
assert(rp != NULL); |
202 |
|
|
|
203 |
✓✓ |
26537 |
if ( ! strcmp(p, "_")) { |
204 |
|
333 |
sp = newspan(tbl, ln, rp); |
205 |
|
333 |
sp->pos = TBL_SPAN_HORIZ; |
206 |
|
333 |
return; |
207 |
✓✓ |
26204 |
} else if ( ! strcmp(p, "=")) { |
208 |
|
30 |
sp = newspan(tbl, ln, rp); |
209 |
|
30 |
sp->pos = TBL_SPAN_DHORIZ; |
210 |
|
30 |
return; |
211 |
|
|
} |
212 |
|
|
|
213 |
|
|
/* |
214 |
|
|
* If the layout row contains nothing but horizontal lines, |
215 |
|
|
* allocate an empty span for it and assign the current span |
216 |
|
|
* to the next layout row accepting data. |
217 |
|
|
*/ |
218 |
|
|
|
219 |
✓✓ |
26258 |
while (rp->next != NULL) { |
220 |
✓✓ |
13666 |
if (rp->last->col + 1 < tbl->opts.cols) |
221 |
|
|
break; |
222 |
✓✓ |
29498 |
for (cp = rp->first; cp != NULL; cp = cp->next) |
223 |
✓✓✗✓
|
28319 |
if (cp->pos != TBL_CELL_HORIZ && |
224 |
|
13612 |
cp->pos != TBL_CELL_DHORIZ) |
225 |
|
|
break; |
226 |
✓✓ |
13654 |
if (cp != NULL) |
227 |
|
|
break; |
228 |
|
42 |
sp = newspan(tbl, ln, rp); |
229 |
|
42 |
sp->pos = TBL_SPAN_DATA; |
230 |
|
42 |
rp = rp->next; |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
/* Process a real data row. */ |
234 |
|
|
|
235 |
|
26174 |
sp = newspan(tbl, ln, rp); |
236 |
|
26174 |
sp->pos = TBL_SPAN_DATA; |
237 |
✓✓ |
189262 |
while (p[pos] != '\0') |
238 |
|
68457 |
getdata(tbl, sp, ln, p, &pos); |
239 |
|
52711 |
} |