GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: eqn.c,v 1.41 2017/07/15 15:06:31 bentley Exp $ */ |
||
2 |
/* |
||
3 |
* Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> |
||
4 |
* Copyright (c) 2014, 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 <limits.h> |
||
23 |
#include <stdio.h> |
||
24 |
#include <stdlib.h> |
||
25 |
#include <string.h> |
||
26 |
#include <time.h> |
||
27 |
|||
28 |
#include "mandoc_aux.h" |
||
29 |
#include "mandoc.h" |
||
30 |
#include "roff.h" |
||
31 |
#include "libmandoc.h" |
||
32 |
#include "libroff.h" |
||
33 |
|||
34 |
#define EQN_NEST_MAX 128 /* maximum nesting of defines */ |
||
35 |
#define STRNEQ(p1, sz1, p2, sz2) \ |
||
36 |
((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1))) |
||
37 |
|||
38 |
enum eqn_tok { |
||
39 |
EQN_TOK_DYAD = 0, |
||
40 |
EQN_TOK_VEC, |
||
41 |
EQN_TOK_UNDER, |
||
42 |
EQN_TOK_BAR, |
||
43 |
EQN_TOK_TILDE, |
||
44 |
EQN_TOK_HAT, |
||
45 |
EQN_TOK_DOT, |
||
46 |
EQN_TOK_DOTDOT, |
||
47 |
EQN_TOK_FWD, |
||
48 |
EQN_TOK_BACK, |
||
49 |
EQN_TOK_DOWN, |
||
50 |
EQN_TOK_UP, |
||
51 |
EQN_TOK_FAT, |
||
52 |
EQN_TOK_ROMAN, |
||
53 |
EQN_TOK_ITALIC, |
||
54 |
EQN_TOK_BOLD, |
||
55 |
EQN_TOK_SIZE, |
||
56 |
EQN_TOK_SUB, |
||
57 |
EQN_TOK_SUP, |
||
58 |
EQN_TOK_SQRT, |
||
59 |
EQN_TOK_OVER, |
||
60 |
EQN_TOK_FROM, |
||
61 |
EQN_TOK_TO, |
||
62 |
EQN_TOK_BRACE_OPEN, |
||
63 |
EQN_TOK_BRACE_CLOSE, |
||
64 |
EQN_TOK_GSIZE, |
||
65 |
EQN_TOK_GFONT, |
||
66 |
EQN_TOK_MARK, |
||
67 |
EQN_TOK_LINEUP, |
||
68 |
EQN_TOK_LEFT, |
||
69 |
EQN_TOK_RIGHT, |
||
70 |
EQN_TOK_PILE, |
||
71 |
EQN_TOK_LPILE, |
||
72 |
EQN_TOK_RPILE, |
||
73 |
EQN_TOK_CPILE, |
||
74 |
EQN_TOK_MATRIX, |
||
75 |
EQN_TOK_CCOL, |
||
76 |
EQN_TOK_LCOL, |
||
77 |
EQN_TOK_RCOL, |
||
78 |
EQN_TOK_DELIM, |
||
79 |
EQN_TOK_DEFINE, |
||
80 |
EQN_TOK_TDEFINE, |
||
81 |
EQN_TOK_NDEFINE, |
||
82 |
EQN_TOK_UNDEF, |
||
83 |
EQN_TOK_ABOVE, |
||
84 |
EQN_TOK__MAX, |
||
85 |
EQN_TOK_FUNC, |
||
86 |
EQN_TOK_QUOTED, |
||
87 |
EQN_TOK_SYM, |
||
88 |
EQN_TOK_EOF |
||
89 |
}; |
||
90 |
|||
91 |
static const char *eqn_toks[EQN_TOK__MAX] = { |
||
92 |
"dyad", /* EQN_TOK_DYAD */ |
||
93 |
"vec", /* EQN_TOK_VEC */ |
||
94 |
"under", /* EQN_TOK_UNDER */ |
||
95 |
"bar", /* EQN_TOK_BAR */ |
||
96 |
"tilde", /* EQN_TOK_TILDE */ |
||
97 |
"hat", /* EQN_TOK_HAT */ |
||
98 |
"dot", /* EQN_TOK_DOT */ |
||
99 |
"dotdot", /* EQN_TOK_DOTDOT */ |
||
100 |
"fwd", /* EQN_TOK_FWD * */ |
||
101 |
"back", /* EQN_TOK_BACK */ |
||
102 |
"down", /* EQN_TOK_DOWN */ |
||
103 |
"up", /* EQN_TOK_UP */ |
||
104 |
"fat", /* EQN_TOK_FAT */ |
||
105 |
"roman", /* EQN_TOK_ROMAN */ |
||
106 |
"italic", /* EQN_TOK_ITALIC */ |
||
107 |
"bold", /* EQN_TOK_BOLD */ |
||
108 |
"size", /* EQN_TOK_SIZE */ |
||
109 |
"sub", /* EQN_TOK_SUB */ |
||
110 |
"sup", /* EQN_TOK_SUP */ |
||
111 |
"sqrt", /* EQN_TOK_SQRT */ |
||
112 |
"over", /* EQN_TOK_OVER */ |
||
113 |
"from", /* EQN_TOK_FROM */ |
||
114 |
"to", /* EQN_TOK_TO */ |
||
115 |
"{", /* EQN_TOK_BRACE_OPEN */ |
||
116 |
"}", /* EQN_TOK_BRACE_CLOSE */ |
||
117 |
"gsize", /* EQN_TOK_GSIZE */ |
||
118 |
"gfont", /* EQN_TOK_GFONT */ |
||
119 |
"mark", /* EQN_TOK_MARK */ |
||
120 |
"lineup", /* EQN_TOK_LINEUP */ |
||
121 |
"left", /* EQN_TOK_LEFT */ |
||
122 |
"right", /* EQN_TOK_RIGHT */ |
||
123 |
"pile", /* EQN_TOK_PILE */ |
||
124 |
"lpile", /* EQN_TOK_LPILE */ |
||
125 |
"rpile", /* EQN_TOK_RPILE */ |
||
126 |
"cpile", /* EQN_TOK_CPILE */ |
||
127 |
"matrix", /* EQN_TOK_MATRIX */ |
||
128 |
"ccol", /* EQN_TOK_CCOL */ |
||
129 |
"lcol", /* EQN_TOK_LCOL */ |
||
130 |
"rcol", /* EQN_TOK_RCOL */ |
||
131 |
"delim", /* EQN_TOK_DELIM */ |
||
132 |
"define", /* EQN_TOK_DEFINE */ |
||
133 |
"tdefine", /* EQN_TOK_TDEFINE */ |
||
134 |
"ndefine", /* EQN_TOK_NDEFINE */ |
||
135 |
"undef", /* EQN_TOK_UNDEF */ |
||
136 |
"above", /* EQN_TOK_ABOVE */ |
||
137 |
}; |
||
138 |
|||
139 |
static const char *const eqn_func[] = { |
||
140 |
"acos", "acsc", "and", "arc", "asec", "asin", "atan", |
||
141 |
"cos", "cosh", "coth", "csc", "det", "exp", "for", |
||
142 |
"if", "lim", "ln", "log", "max", "min", |
||
143 |
"sec", "sin", "sinh", "tan", "tanh", "Im", "Re", |
||
144 |
}; |
||
145 |
|||
146 |
enum eqn_symt { |
||
147 |
EQNSYM_alpha = 0, |
||
148 |
EQNSYM_beta, |
||
149 |
EQNSYM_chi, |
||
150 |
EQNSYM_delta, |
||
151 |
EQNSYM_epsilon, |
||
152 |
EQNSYM_eta, |
||
153 |
EQNSYM_gamma, |
||
154 |
EQNSYM_iota, |
||
155 |
EQNSYM_kappa, |
||
156 |
EQNSYM_lambda, |
||
157 |
EQNSYM_mu, |
||
158 |
EQNSYM_nu, |
||
159 |
EQNSYM_omega, |
||
160 |
EQNSYM_omicron, |
||
161 |
EQNSYM_phi, |
||
162 |
EQNSYM_pi, |
||
163 |
EQNSYM_ps, |
||
164 |
EQNSYM_rho, |
||
165 |
EQNSYM_sigma, |
||
166 |
EQNSYM_tau, |
||
167 |
EQNSYM_theta, |
||
168 |
EQNSYM_upsilon, |
||
169 |
EQNSYM_xi, |
||
170 |
EQNSYM_zeta, |
||
171 |
EQNSYM_DELTA, |
||
172 |
EQNSYM_GAMMA, |
||
173 |
EQNSYM_LAMBDA, |
||
174 |
EQNSYM_OMEGA, |
||
175 |
EQNSYM_PHI, |
||
176 |
EQNSYM_PI, |
||
177 |
EQNSYM_PSI, |
||
178 |
EQNSYM_SIGMA, |
||
179 |
EQNSYM_THETA, |
||
180 |
EQNSYM_UPSILON, |
||
181 |
EQNSYM_XI, |
||
182 |
EQNSYM_inter, |
||
183 |
EQNSYM_union, |
||
184 |
EQNSYM_prod, |
||
185 |
EQNSYM_int, |
||
186 |
EQNSYM_sum, |
||
187 |
EQNSYM_grad, |
||
188 |
EQNSYM_del, |
||
189 |
EQNSYM_times, |
||
190 |
EQNSYM_cdot, |
||
191 |
EQNSYM_nothing, |
||
192 |
EQNSYM_approx, |
||
193 |
EQNSYM_prime, |
||
194 |
EQNSYM_half, |
||
195 |
EQNSYM_partial, |
||
196 |
EQNSYM_inf, |
||
197 |
EQNSYM_muchgreat, |
||
198 |
EQNSYM_muchless, |
||
199 |
EQNSYM_larrow, |
||
200 |
EQNSYM_rarrow, |
||
201 |
EQNSYM_pm, |
||
202 |
EQNSYM_nequal, |
||
203 |
EQNSYM_equiv, |
||
204 |
EQNSYM_lessequal, |
||
205 |
EQNSYM_moreequal, |
||
206 |
EQNSYM_minus, |
||
207 |
EQNSYM__MAX |
||
208 |
}; |
||
209 |
|||
210 |
struct eqnsym { |
||
211 |
const char *str; |
||
212 |
const char *sym; |
||
213 |
}; |
||
214 |
|||
215 |
static const struct eqnsym eqnsyms[EQNSYM__MAX] = { |
||
216 |
{ "alpha", "*a" }, /* EQNSYM_alpha */ |
||
217 |
{ "beta", "*b" }, /* EQNSYM_beta */ |
||
218 |
{ "chi", "*x" }, /* EQNSYM_chi */ |
||
219 |
{ "delta", "*d" }, /* EQNSYM_delta */ |
||
220 |
{ "epsilon", "*e" }, /* EQNSYM_epsilon */ |
||
221 |
{ "eta", "*y" }, /* EQNSYM_eta */ |
||
222 |
{ "gamma", "*g" }, /* EQNSYM_gamma */ |
||
223 |
{ "iota", "*i" }, /* EQNSYM_iota */ |
||
224 |
{ "kappa", "*k" }, /* EQNSYM_kappa */ |
||
225 |
{ "lambda", "*l" }, /* EQNSYM_lambda */ |
||
226 |
{ "mu", "*m" }, /* EQNSYM_mu */ |
||
227 |
{ "nu", "*n" }, /* EQNSYM_nu */ |
||
228 |
{ "omega", "*w" }, /* EQNSYM_omega */ |
||
229 |
{ "omicron", "*o" }, /* EQNSYM_omicron */ |
||
230 |
{ "phi", "*f" }, /* EQNSYM_phi */ |
||
231 |
{ "pi", "*p" }, /* EQNSYM_pi */ |
||
232 |
{ "psi", "*q" }, /* EQNSYM_psi */ |
||
233 |
{ "rho", "*r" }, /* EQNSYM_rho */ |
||
234 |
{ "sigma", "*s" }, /* EQNSYM_sigma */ |
||
235 |
{ "tau", "*t" }, /* EQNSYM_tau */ |
||
236 |
{ "theta", "*h" }, /* EQNSYM_theta */ |
||
237 |
{ "upsilon", "*u" }, /* EQNSYM_upsilon */ |
||
238 |
{ "xi", "*c" }, /* EQNSYM_xi */ |
||
239 |
{ "zeta", "*z" }, /* EQNSYM_zeta */ |
||
240 |
{ "DELTA", "*D" }, /* EQNSYM_DELTA */ |
||
241 |
{ "GAMMA", "*G" }, /* EQNSYM_GAMMA */ |
||
242 |
{ "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */ |
||
243 |
{ "OMEGA", "*W" }, /* EQNSYM_OMEGA */ |
||
244 |
{ "PHI", "*F" }, /* EQNSYM_PHI */ |
||
245 |
{ "PI", "*P" }, /* EQNSYM_PI */ |
||
246 |
{ "PSI", "*Q" }, /* EQNSYM_PSI */ |
||
247 |
{ "SIGMA", "*S" }, /* EQNSYM_SIGMA */ |
||
248 |
{ "THETA", "*H" }, /* EQNSYM_THETA */ |
||
249 |
{ "UPSILON", "*U" }, /* EQNSYM_UPSILON */ |
||
250 |
{ "XI", "*C" }, /* EQNSYM_XI */ |
||
251 |
{ "inter", "ca" }, /* EQNSYM_inter */ |
||
252 |
{ "union", "cu" }, /* EQNSYM_union */ |
||
253 |
{ "prod", "product" }, /* EQNSYM_prod */ |
||
254 |
{ "int", "integral" }, /* EQNSYM_int */ |
||
255 |
{ "sum", "sum" }, /* EQNSYM_sum */ |
||
256 |
{ "grad", "gr" }, /* EQNSYM_grad */ |
||
257 |
{ "del", "gr" }, /* EQNSYM_del */ |
||
258 |
{ "times", "mu" }, /* EQNSYM_times */ |
||
259 |
{ "cdot", "pc" }, /* EQNSYM_cdot */ |
||
260 |
{ "nothing", "&" }, /* EQNSYM_nothing */ |
||
261 |
{ "approx", "~~" }, /* EQNSYM_approx */ |
||
262 |
{ "prime", "fm" }, /* EQNSYM_prime */ |
||
263 |
{ "half", "12" }, /* EQNSYM_half */ |
||
264 |
{ "partial", "pd" }, /* EQNSYM_partial */ |
||
265 |
{ "inf", "if" }, /* EQNSYM_inf */ |
||
266 |
{ ">>", ">>" }, /* EQNSYM_muchgreat */ |
||
267 |
{ "<<", "<<" }, /* EQNSYM_muchless */ |
||
268 |
{ "<-", "<-" }, /* EQNSYM_larrow */ |
||
269 |
{ "->", "->" }, /* EQNSYM_rarrow */ |
||
270 |
{ "+-", "+-" }, /* EQNSYM_pm */ |
||
271 |
{ "!=", "!=" }, /* EQNSYM_nequal */ |
||
272 |
{ "==", "==" }, /* EQNSYM_equiv */ |
||
273 |
{ "<=", "<=" }, /* EQNSYM_lessequal */ |
||
274 |
{ ">=", ">=" }, /* EQNSYM_moreequal */ |
||
275 |
{ "-", "mi" }, /* EQNSYM_minus */ |
||
276 |
}; |
||
277 |
|||
278 |
enum parse_mode { |
||
279 |
MODE_QUOTED, |
||
280 |
MODE_NOSUB, |
||
281 |
MODE_SUB, |
||
282 |
MODE_TOK |
||
283 |
}; |
||
284 |
|||
285 |
static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *); |
||
286 |
static struct eqn_box *eqn_box_makebinary(struct eqn_node *, |
||
287 |
struct eqn_box *); |
||
288 |
static void eqn_def(struct eqn_node *); |
||
289 |
static struct eqn_def *eqn_def_find(struct eqn_node *); |
||
290 |
static void eqn_delim(struct eqn_node *); |
||
291 |
static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode); |
||
292 |
static void eqn_undef(struct eqn_node *); |
||
293 |
|||
294 |
|||
295 |
struct eqn_node * |
||
296 |
eqn_alloc(struct mparse *parse) |
||
297 |
{ |
||
298 |
struct eqn_node *ep; |
||
299 |
|||
300 |
780 |
ep = mandoc_calloc(1, sizeof(*ep)); |
|
301 |
390 |
ep->parse = parse; |
|
302 |
390 |
ep->gsize = EQN_DEFSIZE; |
|
303 |
390 |
return ep; |
|
304 |
} |
||
305 |
|||
306 |
void |
||
307 |
eqn_reset(struct eqn_node *ep) |
||
308 |
{ |
||
309 |
330 |
free(ep->data); |
|
310 |
165 |
ep->data = ep->start = ep->end = NULL; |
|
311 |
165 |
ep->sz = ep->toksz = 0; |
|
312 |
165 |
} |
|
313 |
|||
314 |
void |
||
315 |
eqn_read(struct eqn_node *ep, const char *p) |
||
316 |
{ |
||
317 |
3030 |
char *cp; |
|
318 |
|||
319 |
✓✓ | 1515 |
if (ep->data == NULL) { |
320 |
555 |
ep->sz = strlen(p); |
|
321 |
555 |
ep->data = mandoc_strdup(p); |
|
322 |
555 |
} else { |
|
323 |
960 |
ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p); |
|
324 |
960 |
free(ep->data); |
|
325 |
960 |
ep->data = cp; |
|
326 |
} |
||
327 |
1515 |
ep->sz += 1; |
|
328 |
1515 |
} |
|
329 |
|||
330 |
/* |
||
331 |
* Find the key "key" of the give size within our eqn-defined values. |
||
332 |
*/ |
||
333 |
static struct eqn_def * |
||
334 |
eqn_def_find(struct eqn_node *ep) |
||
335 |
{ |
||
336 |
int i; |
||
337 |
|||
338 |
✓✓ | 37737 |
for (i = 0; i < (int)ep->defsz; i++) |
339 |
✓✓✓✓ ✓✓ |
15669 |
if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, |
340 |
ep->defs[i].keysz, ep->start, ep->toksz)) |
||
341 |
4878 |
return &ep->defs[i]; |
|
342 |
|||
343 |
7299 |
return NULL; |
|
344 |
12177 |
} |
|
345 |
|||
346 |
/* |
||
347 |
* Parse a token from the input text. The modes are: |
||
348 |
* MODE_QUOTED: Use *ep->start as the delimiter; the token ends |
||
349 |
* before its next occurence. Do not interpret the token in any |
||
350 |
* way and return EQN_TOK_QUOTED. All other modes behave like |
||
351 |
* MODE_QUOTED when *ep->start is '"'. |
||
352 |
* MODE_NOSUB: If *ep->start is a curly brace, the token ends after it; |
||
353 |
* otherwise, it ends before the next whitespace or brace. |
||
354 |
* Do not interpret the token and return EQN_TOK__MAX. |
||
355 |
* MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an |
||
356 |
* alias created with define. If it is an alias, replace it with |
||
357 |
* its string value and reparse. |
||
358 |
* MODE_TOK: Like MODE_SUB, but also check the token against the list |
||
359 |
* of tokens, and if there is a match, return that token. Otherwise, |
||
360 |
* if the token matches a symbol, return EQN_TOK_SYM; if it matches |
||
361 |
* a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for |
||
362 |
* a token match, *ep->start is set to an allocated string that the |
||
363 |
* caller is expected to free. |
||
364 |
* All modes skip whitespace following the end of the token. |
||
365 |
*/ |
||
366 |
static enum eqn_tok |
||
367 |
eqn_next(struct eqn_node *ep, enum parse_mode mode) |
||
368 |
{ |
||
369 |
static int last_len, lim; |
||
370 |
|||
371 |
struct eqn_def *def; |
||
372 |
size_t start; |
||
373 |
int diff, i, quoted; |
||
374 |
enum eqn_tok tok; |
||
375 |
|||
376 |
/* |
||
377 |
* Reset the recursion counter after advancing |
||
378 |
* beyond the end of the previous substitution. |
||
379 |
*/ |
||
380 |
✓✓ | 18084 |
if (ep->end - ep->data >= last_len) |
381 |
8529 |
lim = 0; |
|
382 |
|||
383 |
9042 |
ep->start = ep->end; |
|
384 |
9042 |
quoted = mode == MODE_QUOTED; |
|
385 |
13740 |
for (;;) { |
|
386 |
✓✓✓ | 14550 |
switch (*ep->start) { |
387 |
case '\0': |
||
388 |
573 |
ep->toksz = 0; |
|
389 |
573 |
return EQN_TOK_EOF; |
|
390 |
case '"': |
||
391 |
quoted = 1; |
||
392 |
810 |
break; |
|
393 |
default: |
||
394 |
break; |
||
395 |
} |
||
396 |
✓✓ | 13167 |
if (quoted) { |
397 |
972 |
ep->end = strchr(ep->start + 1, *ep->start); |
|
398 |
972 |
ep->start++; /* Skip opening quote. */ |
|
399 |
✗✓ | 972 |
if (ep->end == NULL) { |
400 |
mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse, |
||
401 |
ep->node->line, ep->node->pos, NULL); |
||
402 |
ep->end = strchr(ep->start, '\0'); |
||
403 |
} |
||
404 |
} else { |
||
405 |
12195 |
ep->end = ep->start + 1; |
|
406 |
✓✓✓✓ |
24126 |
if (*ep->start != '{' && *ep->start != '}') |
407 |
11667 |
ep->end += strcspn(ep->end, " ^~\"{}\t"); |
|
408 |
} |
||
409 |
13167 |
ep->toksz = ep->end - ep->start; |
|
410 |
✓✓✓✗ |
14139 |
if (quoted && *ep->end != '\0') |
411 |
972 |
ep->end++; /* Skip closing quote. */ |
|
412 |
✓✓✓✓ |
54357 |
while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL) |
413 |
10299 |
ep->end++; |
|
414 |
✓✓ | 13167 |
if (quoted) /* Cannot return, may have to strndup. */ |
415 |
break; |
||
416 |
✓✓ | 12195 |
if (mode == MODE_NOSUB) |
417 |
252 |
return EQN_TOK__MAX; |
|
418 |
✓✓ | 11943 |
if ((def = eqn_def_find(ep)) == NULL) |
419 |
break; |
||
420 |
✓✓ | 4770 |
if (++lim > EQN_NEST_MAX) { |
421 |
144 |
mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse, |
|
422 |
72 |
ep->node->line, ep->node->pos, NULL); |
|
423 |
72 |
return EQN_TOK_EOF; |
|
424 |
} |
||
425 |
|||
426 |
/* Replace a defined name with its string value. */ |
||
427 |
✓✓ | 4698 |
if ((diff = def->valsz - ep->toksz) > 0) { |
428 |
2313 |
start = ep->start - ep->data; |
|
429 |
2313 |
ep->sz += diff; |
|
430 |
2313 |
ep->data = mandoc_realloc(ep->data, ep->sz + 1); |
|
431 |
2313 |
ep->start = ep->data + start; |
|
432 |
2313 |
} |
|
433 |
✓✓ | 4698 |
if (diff) |
434 |
4770 |
memmove(ep->start + def->valsz, ep->start + ep->toksz, |
|
435 |
2385 |
strlen(ep->start + ep->toksz) + 1); |
|
436 |
4698 |
memcpy(ep->start, def->val, def->valsz); |
|
437 |
4698 |
last_len = ep->start - ep->data + def->valsz; |
|
438 |
} |
||
439 |
✓✓ | 8145 |
if (mode != MODE_TOK) |
440 |
423 |
return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX; |
|
441 |
✓✓ | 7722 |
if (quoted) { |
442 |
810 |
ep->start = mandoc_strndup(ep->start, ep->toksz); |
|
443 |
810 |
return EQN_TOK_QUOTED; |
|
444 |
} |
||
445 |
✓✓ | 471738 |
for (tok = 0; tok < EQN_TOK__MAX; tok++) |
446 |
✓✓✓✓ |
264456 |
if (STRNEQ(ep->start, ep->toksz, |
447 |
eqn_toks[tok], strlen(eqn_toks[tok]))) |
||
448 |
3426 |
return tok; |
|
449 |
|||
450 |
✓✓ | 413490 |
for (i = 0; i < EQNSYM__MAX; i++) { |
451 |
✓✓✓✓ |
217206 |
if (STRNEQ(ep->start, ep->toksz, |
452 |
eqnsyms[i].str, strlen(eqnsyms[i].str))) { |
||
453 |
186 |
mandoc_asprintf(&ep->start, |
|
454 |
186 |
"\\[%s]", eqnsyms[i].sym); |
|
455 |
186 |
return EQN_TOK_SYM; |
|
456 |
} |
||
457 |
} |
||
458 |
3300 |
ep->start = mandoc_strndup(ep->start, ep->toksz); |
|
459 |
✓✓ | 170148 |
for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++) |
460 |
✓✓✓✓ |
88278 |
if (STRNEQ(ep->start, ep->toksz, |
461 |
eqn_func[i], strlen(eqn_func[i]))) |
||
462 |
579 |
return EQN_TOK_FUNC; |
|
463 |
2721 |
return EQN_TOK__MAX; |
|
464 |
9042 |
} |
|
465 |
|||
466 |
void |
||
467 |
eqn_box_free(struct eqn_box *bp) |
||
468 |
{ |
||
469 |
|||
470 |
✓✓ | 15036 |
if (bp->first) |
471 |
2886 |
eqn_box_free(bp->first); |
|
472 |
✓✓ | 7518 |
if (bp->next) |
473 |
4077 |
eqn_box_free(bp->next); |
|
474 |
|||
475 |
7518 |
free(bp->text); |
|
476 |
7518 |
free(bp->left); |
|
477 |
7518 |
free(bp->right); |
|
478 |
7518 |
free(bp->top); |
|
479 |
7518 |
free(bp->bottom); |
|
480 |
7518 |
free(bp); |
|
481 |
7518 |
} |
|
482 |
|||
483 |
/* |
||
484 |
* Allocate a box as the last child of the parent node. |
||
485 |
*/ |
||
486 |
static struct eqn_box * |
||
487 |
eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) |
||
488 |
{ |
||
489 |
struct eqn_box *bp; |
||
490 |
|||
491 |
13926 |
bp = mandoc_calloc(1, sizeof(struct eqn_box)); |
|
492 |
6963 |
bp->parent = parent; |
|
493 |
6963 |
bp->parent->args++; |
|
494 |
6963 |
bp->expectargs = UINT_MAX; |
|
495 |
6963 |
bp->font = bp->parent->font; |
|
496 |
6963 |
bp->size = ep->gsize; |
|
497 |
|||
498 |
✓✓ | 6963 |
if (NULL != parent->first) { |
499 |
4923 |
parent->last->next = bp; |
|
500 |
4923 |
bp->prev = parent->last; |
|
501 |
4923 |
} else |
|
502 |
2040 |
parent->first = bp; |
|
503 |
|||
504 |
6963 |
parent->last = bp; |
|
505 |
6963 |
return bp; |
|
506 |
} |
||
507 |
|||
508 |
/* |
||
509 |
* Reparent the current last node (of the current parent) under a new |
||
510 |
* EQN_SUBEXPR as the first element. |
||
511 |
* Then return the new parent. |
||
512 |
* The new EQN_SUBEXPR will have a two-child limit. |
||
513 |
*/ |
||
514 |
static struct eqn_box * |
||
515 |
eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent) |
||
516 |
{ |
||
517 |
struct eqn_box *b, *newb; |
||
518 |
|||
519 |
✗✓ | 2592 |
assert(NULL != parent->last); |
520 |
b = parent->last; |
||
521 |
✓✓ | 1296 |
if (parent->last == parent->first) |
522 |
453 |
parent->first = NULL; |
|
523 |
1296 |
parent->args--; |
|
524 |
1296 |
parent->last = b->prev; |
|
525 |
1296 |
b->prev = NULL; |
|
526 |
1296 |
newb = eqn_box_alloc(ep, parent); |
|
527 |
1296 |
newb->type = EQN_SUBEXPR; |
|
528 |
1296 |
newb->expectargs = 2; |
|
529 |
1296 |
newb->args = 1; |
|
530 |
1296 |
newb->first = newb->last = b; |
|
531 |
1296 |
newb->first->next = NULL; |
|
532 |
1296 |
b->parent = newb; |
|
533 |
1296 |
return newb; |
|
534 |
} |
||
535 |
|||
536 |
/* |
||
537 |
* Parse the "delim" control statement. |
||
538 |
*/ |
||
539 |
static void |
||
540 |
eqn_delim(struct eqn_node *ep) |
||
541 |
{ |
||
542 |
✓✗✗✓ |
90 |
if (ep->end[0] == '\0' || ep->end[1] == '\0') { |
543 |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
||
544 |
ep->node->line, ep->node->pos, "delim"); |
||
545 |
if (ep->end[0] != '\0') |
||
546 |
ep->end++; |
||
547 |
✓✓ | 30 |
} else if (strncmp(ep->end, "off", 3) == 0) { |
548 |
9 |
ep->delim = 0; |
|
549 |
9 |
ep->end += 3; |
|
550 |
✓✓ | 30 |
} else if (strncmp(ep->end, "on", 2) == 0) { |
551 |
✓✗✓✗ |
18 |
if (ep->odelim && ep->cdelim) |
552 |
9 |
ep->delim = 1; |
|
553 |
9 |
ep->end += 2; |
|
554 |
9 |
} else { |
|
555 |
12 |
ep->odelim = *ep->end++; |
|
556 |
12 |
ep->cdelim = *ep->end++; |
|
557 |
12 |
ep->delim = 1; |
|
558 |
} |
||
559 |
30 |
} |
|
560 |
|||
561 |
/* |
||
562 |
* Undefine a previously-defined string. |
||
563 |
*/ |
||
564 |
static void |
||
565 |
eqn_undef(struct eqn_node *ep) |
||
566 |
{ |
||
567 |
struct eqn_def *def; |
||
568 |
|||
569 |
✓✓ | 144 |
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { |
570 |
36 |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
|
571 |
18 |
ep->node->line, ep->node->pos, "undef"); |
|
572 |
18 |
return; |
|
573 |
} |
||
574 |
✗✓ | 54 |
if ((def = eqn_def_find(ep)) == NULL) |
575 |
return; |
||
576 |
54 |
free(def->key); |
|
577 |
54 |
free(def->val); |
|
578 |
54 |
def->key = def->val = NULL; |
|
579 |
54 |
def->keysz = def->valsz = 0; |
|
580 |
126 |
} |
|
581 |
|||
582 |
static void |
||
583 |
eqn_def(struct eqn_node *ep) |
||
584 |
{ |
||
585 |
struct eqn_def *def; |
||
586 |
int i; |
||
587 |
|||
588 |
✓✓ | 396 |
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { |
589 |
36 |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
|
590 |
18 |
ep->node->line, ep->node->pos, "define"); |
|
591 |
18 |
return; |
|
592 |
} |
||
593 |
|||
594 |
/* |
||
595 |
* Search for a key that already exists. |
||
596 |
* Create a new key if none is found. |
||
597 |
*/ |
||
598 |
✓✓ | 180 |
if ((def = eqn_def_find(ep)) == NULL) { |
599 |
/* Find holes in string array. */ |
||
600 |
✓✓ | 288 |
for (i = 0; i < (int)ep->defsz; i++) |
601 |
✓✓ | 72 |
if (0 == ep->defs[i].keysz) |
602 |
break; |
||
603 |
|||
604 |
✓✓ | 126 |
if (i == (int)ep->defsz) { |
605 |
72 |
ep->defsz++; |
|
606 |
72 |
ep->defs = mandoc_reallocarray(ep->defs, |
|
607 |
ep->defsz, sizeof(struct eqn_def)); |
||
608 |
72 |
ep->defs[i].key = ep->defs[i].val = NULL; |
|
609 |
72 |
} |
|
610 |
|||
611 |
126 |
def = ep->defs + i; |
|
612 |
126 |
free(def->key); |
|
613 |
126 |
def->key = mandoc_strndup(ep->start, ep->toksz); |
|
614 |
126 |
def->keysz = ep->toksz; |
|
615 |
126 |
} |
|
616 |
|||
617 |
✓✓ | 180 |
if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) { |
618 |
36 |
mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse, |
|
619 |
18 |
ep->node->line, ep->node->pos, "define %s", def->key); |
|
620 |
18 |
free(def->key); |
|
621 |
18 |
free(def->val); |
|
622 |
18 |
def->key = def->val = NULL; |
|
623 |
18 |
def->keysz = def->valsz = 0; |
|
624 |
18 |
return; |
|
625 |
} |
||
626 |
162 |
free(def->val); |
|
627 |
162 |
def->val = mandoc_strndup(ep->start, ep->toksz); |
|
628 |
162 |
def->valsz = ep->toksz; |
|
629 |
360 |
} |
|
630 |
|||
631 |
void |
||
632 |
eqn_parse(struct eqn_node *ep) |
||
633 |
{ |
||
634 |
struct eqn_box *cur, *nbox, *parent, *split; |
||
635 |
1110 |
const char *cp, *cpn; |
|
636 |
char *p; |
||
637 |
enum eqn_tok tok; |
||
638 |
enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln; |
||
639 |
int size; |
||
640 |
|||
641 |
555 |
parent = ep->node->eqn; |
|
642 |
✗✓ | 555 |
assert(parent != NULL); |
643 |
|||
644 |
/* |
||
645 |
* Empty equation. |
||
646 |
* Do not add it to the high-level syntax tree. |
||
647 |
*/ |
||
648 |
|||
649 |
✗✓ | 555 |
if (ep->data == NULL) |
650 |
return; |
||
651 |
|||
652 |
555 |
ep->start = ep->end = ep->data + strspn(ep->data, " ^~"); |
|
653 |
|||
654 |
next_tok: |
||
655 |
10380 |
tok = eqn_next(ep, MODE_TOK); |
|
656 |
✗✓✓✗ ✓✓✓✗ ✗✗✗✗ ✗✗✗✓ ✗✗✗✗ ✗✗✗✓ ✗✓✗✗ ✗✓✓✓ ✗✓✗✓ ✗✗✗✗ ✗✗✓✓ ✓✓✗✗ ✗✓✗ |
10380 |
switch (tok) { |
657 |
case EQN_TOK_UNDEF: |
||
658 |
72 |
eqn_undef(ep); |
|
659 |
72 |
break; |
|
660 |
case EQN_TOK_NDEFINE: |
||
661 |
case EQN_TOK_DEFINE: |
||
662 |
198 |
eqn_def(ep); |
|
663 |
198 |
break; |
|
664 |
case EQN_TOK_TDEFINE: |
||
665 |
✓✓✗✓ |
54 |
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF || |
666 |
18 |
eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) |
|
667 |
72 |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
|
668 |
36 |
ep->node->line, ep->node->pos, "tdefine"); |
|
669 |
break; |
||
670 |
case EQN_TOK_DELIM: |
||
671 |
30 |
eqn_delim(ep); |
|
672 |
30 |
break; |
|
673 |
case EQN_TOK_GFONT: |
||
674 |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) |
||
675 |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
||
676 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
||
677 |
break; |
||
678 |
case EQN_TOK_MARK: |
||
679 |
case EQN_TOK_LINEUP: |
||
680 |
/* Ignore these. */ |
||
681 |
break; |
||
682 |
case EQN_TOK_DYAD: |
||
683 |
case EQN_TOK_VEC: |
||
684 |
case EQN_TOK_UNDER: |
||
685 |
case EQN_TOK_BAR: |
||
686 |
case EQN_TOK_TILDE: |
||
687 |
case EQN_TOK_HAT: |
||
688 |
case EQN_TOK_DOT: |
||
689 |
case EQN_TOK_DOTDOT: |
||
690 |
✗✓ | 396 |
if (parent->last == NULL) { |
691 |
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, |
||
692 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
||
693 |
cur = eqn_box_alloc(ep, parent); |
||
694 |
cur->type = EQN_TEXT; |
||
695 |
cur->text = mandoc_strdup(""); |
||
696 |
} |
||
697 |
396 |
parent = eqn_box_makebinary(ep, parent); |
|
698 |
396 |
parent->type = EQN_LIST; |
|
699 |
396 |
parent->expectargs = 1; |
|
700 |
396 |
parent->font = EQNFONT_ROMAN; |
|
701 |
✓✓✓✓ ✓✓✓✓ ✗ |
396 |
switch (tok) { |
702 |
case EQN_TOK_DOTDOT: |
||
703 |
18 |
parent->top = mandoc_strdup("\\[ad]"); |
|
704 |
18 |
break; |
|
705 |
case EQN_TOK_VEC: |
||
706 |
18 |
parent->top = mandoc_strdup("\\[->]"); |
|
707 |
18 |
break; |
|
708 |
case EQN_TOK_DYAD: |
||
709 |
36 |
parent->top = mandoc_strdup("\\[<>]"); |
|
710 |
36 |
break; |
|
711 |
case EQN_TOK_TILDE: |
||
712 |
72 |
parent->top = mandoc_strdup("\\[a~]"); |
|
713 |
72 |
break; |
|
714 |
case EQN_TOK_UNDER: |
||
715 |
54 |
parent->bottom = mandoc_strdup("\\[ul]"); |
|
716 |
54 |
break; |
|
717 |
case EQN_TOK_BAR: |
||
718 |
36 |
parent->top = mandoc_strdup("\\[rn]"); |
|
719 |
36 |
break; |
|
720 |
case EQN_TOK_DOT: |
||
721 |
18 |
parent->top = mandoc_strdup("\\[a.]"); |
|
722 |
18 |
break; |
|
723 |
case EQN_TOK_HAT: |
||
724 |
144 |
parent->top = mandoc_strdup("\\[ha]"); |
|
725 |
144 |
break; |
|
726 |
default: |
||
727 |
abort(); |
||
728 |
} |
||
729 |
396 |
parent = parent->parent; |
|
730 |
396 |
break; |
|
731 |
case EQN_TOK_FWD: |
||
732 |
case EQN_TOK_BACK: |
||
733 |
case EQN_TOK_DOWN: |
||
734 |
case EQN_TOK_UP: |
||
735 |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) |
||
736 |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
||
737 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
||
738 |
break; |
||
739 |
case EQN_TOK_FAT: |
||
740 |
case EQN_TOK_ROMAN: |
||
741 |
case EQN_TOK_ITALIC: |
||
742 |
case EQN_TOK_BOLD: |
||
743 |
✓✓ | 534 |
while (parent->args == parent->expectargs) |
744 |
36 |
parent = parent->parent; |
|
745 |
/* |
||
746 |
* These values apply to the next word or sequence of |
||
747 |
* words; thus, we mark that we'll have a child with |
||
748 |
* exactly one of those. |
||
749 |
*/ |
||
750 |
462 |
parent = eqn_box_alloc(ep, parent); |
|
751 |
462 |
parent->type = EQN_LIST; |
|
752 |
462 |
parent->expectargs = 1; |
|
753 |
✗✓✓✓ ✗ |
462 |
switch (tok) { |
754 |
case EQN_TOK_FAT: |
||
755 |
parent->font = EQNFONT_FAT; |
||
756 |
break; |
||
757 |
case EQN_TOK_ROMAN: |
||
758 |
parent->font = EQNFONT_ROMAN; |
||
759 |
228 |
break; |
|
760 |
case EQN_TOK_ITALIC: |
||
761 |
parent->font = EQNFONT_ITALIC; |
||
762 |
54 |
break; |
|
763 |
case EQN_TOK_BOLD: |
||
764 |
parent->font = EQNFONT_BOLD; |
||
765 |
180 |
break; |
|
766 |
default: |
||
767 |
abort(); |
||
768 |
} |
||
769 |
462 |
break; |
|
770 |
case EQN_TOK_SIZE: |
||
771 |
case EQN_TOK_GSIZE: |
||
772 |
/* Accept two values: integral size and a single. */ |
||
773 |
✗✓ | 27 |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { |
774 |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
||
775 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
||
776 |
break; |
||
777 |
} |
||
778 |
27 |
size = mandoc_strntoi(ep->start, ep->toksz, 10); |
|
779 |
✗✓ | 27 |
if (-1 == size) { |
780 |
mandoc_msg(MANDOCERR_IT_NONUM, ep->parse, |
||
781 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
||
782 |
break; |
||
783 |
} |
||
784 |
✗✓ | 27 |
if (EQN_TOK_GSIZE == tok) { |
785 |
ep->gsize = size; |
||
786 |
break; |
||
787 |
} |
||
788 |
✓✓ | 63 |
while (parent->args == parent->expectargs) |
789 |
18 |
parent = parent->parent; |
|
790 |
27 |
parent = eqn_box_alloc(ep, parent); |
|
791 |
27 |
parent->type = EQN_LIST; |
|
792 |
27 |
parent->expectargs = 1; |
|
793 |
27 |
parent->size = size; |
|
794 |
27 |
break; |
|
795 |
case EQN_TOK_FROM: |
||
796 |
case EQN_TOK_TO: |
||
797 |
case EQN_TOK_SUB: |
||
798 |
case EQN_TOK_SUP: |
||
799 |
/* |
||
800 |
* We have a left-right-associative expression. |
||
801 |
* Repivot under a positional node, open a child scope |
||
802 |
* and keep on reading. |
||
803 |
*/ |
||
804 |
✗✓ | 900 |
if (parent->last == NULL) { |
805 |
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, |
||
806 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
||
807 |
cur = eqn_box_alloc(ep, parent); |
||
808 |
cur->type = EQN_TEXT; |
||
809 |
cur->text = mandoc_strdup(""); |
||
810 |
} |
||
811 |
✓✓✓✗ |
1224 |
while (parent->expectargs == 1 && parent->args == 1) |
812 |
108 |
parent = parent->parent; |
|
813 |
✓✓ | 900 |
if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) { |
814 |
✓✓ | 960 |
for (cur = parent; cur != NULL; cur = cur->parent) |
815 |
✓✗✓✗ |
576 |
if (cur->pos == EQNPOS_SUB || |
816 |
✓✗ | 306 |
cur->pos == EQNPOS_SUP || |
817 |
✓✓ | 306 |
cur->pos == EQNPOS_SUBSUP || |
818 |
✓✗ | 270 |
cur->pos == EQNPOS_SQRT || |
819 |
270 |
cur->pos == EQNPOS_OVER) |
|
820 |
break; |
||
821 |
✓✓ | 210 |
if (cur != NULL) |
822 |
36 |
parent = cur->parent; |
|
823 |
} |
||
824 |
✓✓✓✓ |
1212 |
if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) { |
825 |
126 |
parent->expectargs = 3; |
|
826 |
126 |
parent->pos = EQNPOS_SUBSUP; |
|
827 |
126 |
break; |
|
828 |
} |
||
829 |
✓✓✓✓ |
888 |
if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) { |
830 |
96 |
parent->expectargs = 3; |
|
831 |
96 |
parent->pos = EQNPOS_FROMTO; |
|
832 |
96 |
break; |
|
833 |
} |
||
834 |
678 |
parent = eqn_box_makebinary(ep, parent); |
|
835 |
✓✓✓✓ ✗ |
678 |
switch (tok) { |
836 |
case EQN_TOK_FROM: |
||
837 |
parent->pos = EQNPOS_FROM; |
||
838 |
96 |
break; |
|
839 |
case EQN_TOK_TO: |
||
840 |
parent->pos = EQNPOS_TO; |
||
841 |
18 |
break; |
|
842 |
case EQN_TOK_SUP: |
||
843 |
parent->pos = EQNPOS_SUP; |
||
844 |
186 |
break; |
|
845 |
case EQN_TOK_SUB: |
||
846 |
parent->pos = EQNPOS_SUB; |
||
847 |
378 |
break; |
|
848 |
default: |
||
849 |
abort(); |
||
850 |
} |
||
851 |
678 |
break; |
|
852 |
case EQN_TOK_SQRT: |
||
853 |
✗✓ | 129 |
while (parent->args == parent->expectargs) |
854 |
parent = parent->parent; |
||
855 |
/* |
||
856 |
* Accept a left-right-associative set of arguments just |
||
857 |
* like sub and sup and friends but without rebalancing |
||
858 |
* under a pivot. |
||
859 |
*/ |
||
860 |
129 |
parent = eqn_box_alloc(ep, parent); |
|
861 |
129 |
parent->type = EQN_SUBEXPR; |
|
862 |
129 |
parent->pos = EQNPOS_SQRT; |
|
863 |
129 |
parent->expectargs = 1; |
|
864 |
129 |
break; |
|
865 |
case EQN_TOK_OVER: |
||
866 |
/* |
||
867 |
* We have a right-left-associative fraction. |
||
868 |
* Close out anything that's currently open, then |
||
869 |
* rebalance and continue reading. |
||
870 |
*/ |
||
871 |
✓✓ | 222 |
if (parent->last == NULL) { |
872 |
54 |
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, |
|
873 |
27 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
|
874 |
27 |
cur = eqn_box_alloc(ep, parent); |
|
875 |
27 |
cur->type = EQN_TEXT; |
|
876 |
27 |
cur->text = mandoc_strdup(""); |
|
877 |
27 |
} |
|
878 |
✓✓ | 366 |
while (parent->args == parent->expectargs) |
879 |
72 |
parent = parent->parent; |
|
880 |
✓✓ | 276 |
while (EQN_SUBEXPR == parent->type) |
881 |
27 |
parent = parent->parent; |
|
882 |
222 |
parent = eqn_box_makebinary(ep, parent); |
|
883 |
222 |
parent->pos = EQNPOS_OVER; |
|
884 |
222 |
break; |
|
885 |
case EQN_TOK_RIGHT: |
||
886 |
case EQN_TOK_BRACE_CLOSE: |
||
887 |
/* |
||
888 |
* Close out the existing brace. |
||
889 |
* FIXME: this is a shitty sentinel: we should really |
||
890 |
* have a native EQN_BRACE type or whatnot. |
||
891 |
*/ |
||
892 |
✓✗ | 978 |
for (cur = parent; cur != NULL; cur = cur->parent) |
893 |
✓✓✗✓ |
606 |
if (cur->type == EQN_LIST && |
894 |
✓✗ | 378 |
cur->expectargs > 1 && |
895 |
✓✓ | 378 |
(tok == EQN_TOK_BRACE_CLOSE || |
896 |
117 |
cur->left != NULL)) |
|
897 |
break; |
||
898 |
✗✓ | 378 |
if (cur == NULL) { |
899 |
mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse, |
||
900 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
||
901 |
break; |
||
902 |
} |
||
903 |
parent = cur; |
||
904 |
✓✓ | 378 |
if (EQN_TOK_RIGHT == tok) { |
905 |
✗✓ | 117 |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { |
906 |
mandoc_msg(MANDOCERR_REQ_EMPTY, |
||
907 |
ep->parse, ep->node->line, |
||
908 |
ep->node->pos, eqn_toks[tok]); |
||
909 |
break; |
||
910 |
} |
||
911 |
/* Handling depends on right/left. */ |
||
912 |
✗✓✗✗ |
117 |
if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) |
913 |
parent->right = mandoc_strdup("\\[rc]"); |
||
914 |
✗✓✗✗ |
117 |
else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) |
915 |
parent->right = mandoc_strdup("\\[rf]"); |
||
916 |
else |
||
917 |
parent->right = |
||
918 |
117 |
mandoc_strndup(ep->start, ep->toksz); |
|
919 |
117 |
} |
|
920 |
378 |
parent = parent->parent; |
|
921 |
✓✓✓✓ |
567 |
if (tok == EQN_TOK_BRACE_CLOSE && |
922 |
✓✓ | 261 |
(parent->type == EQN_PILE || |
923 |
189 |
parent->type == EQN_MATRIX)) |
|
924 |
108 |
parent = parent->parent; |
|
925 |
/* Close out any "singleton" lists. */ |
||
926 |
✓✓✗✓ |
486 |
while (parent->type == EQN_LIST && |
927 |
✓✓ | 153 |
parent->expectargs == 1 && |
928 |
36 |
parent->args == 1) |
|
929 |
36 |
parent = parent->parent; |
|
930 |
break; |
||
931 |
case EQN_TOK_BRACE_OPEN: |
||
932 |
case EQN_TOK_LEFT: |
||
933 |
/* |
||
934 |
* If we already have something in the stack and we're |
||
935 |
* in an expression, then rewind til we're not any more |
||
936 |
* (just like with the text node). |
||
937 |
*/ |
||
938 |
✓✓ | 384 |
while (parent->args == parent->expectargs) |
939 |
3 |
parent = parent->parent; |
|
940 |
✓✓✗✓ |
495 |
if (EQN_TOK_LEFT == tok && |
941 |
117 |
eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { |
|
942 |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
||
943 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
||
944 |
break; |
||
945 |
} |
||
946 |
378 |
parent = eqn_box_alloc(ep, parent); |
|
947 |
378 |
parent->type = EQN_LIST; |
|
948 |
✓✓ | 378 |
if (EQN_TOK_LEFT == tok) { |
949 |
✗✓✗✗ |
117 |
if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) |
950 |
parent->left = mandoc_strdup("\\[lc]"); |
||
951 |
✗✓✗✗ |
117 |
else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) |
952 |
parent->left = mandoc_strdup("\\[lf]"); |
||
953 |
else |
||
954 |
parent->left = |
||
955 |
117 |
mandoc_strndup(ep->start, ep->toksz); |
|
956 |
117 |
} |
|
957 |
break; |
||
958 |
case EQN_TOK_PILE: |
||
959 |
case EQN_TOK_LPILE: |
||
960 |
case EQN_TOK_RPILE: |
||
961 |
case EQN_TOK_CPILE: |
||
962 |
case EQN_TOK_CCOL: |
||
963 |
case EQN_TOK_LCOL: |
||
964 |
case EQN_TOK_RCOL: |
||
965 |
✗✓ | 72 |
while (parent->args == parent->expectargs) |
966 |
parent = parent->parent; |
||
967 |
72 |
parent = eqn_box_alloc(ep, parent); |
|
968 |
72 |
parent->type = EQN_PILE; |
|
969 |
72 |
parent->expectargs = 1; |
|
970 |
72 |
break; |
|
971 |
case EQN_TOK_ABOVE: |
||
972 |
✓✗ | 396 |
for (cur = parent; cur != NULL; cur = cur->parent) |
973 |
✓✓ | 198 |
if (cur->type == EQN_PILE) |
974 |
break; |
||
975 |
✗✓ | 72 |
if (cur == NULL) { |
976 |
mandoc_msg(MANDOCERR_IT_STRAY, ep->parse, |
||
977 |
ep->node->line, ep->node->pos, eqn_toks[tok]); |
||
978 |
break; |
||
979 |
} |
||
980 |
72 |
parent = eqn_box_alloc(ep, cur); |
|
981 |
72 |
parent->type = EQN_LIST; |
|
982 |
72 |
break; |
|
983 |
case EQN_TOK_MATRIX: |
||
984 |
✗✓ | 54 |
while (parent->args == parent->expectargs) |
985 |
parent = parent->parent; |
||
986 |
54 |
parent = eqn_box_alloc(ep, parent); |
|
987 |
54 |
parent->type = EQN_MATRIX; |
|
988 |
54 |
parent->expectargs = 1; |
|
989 |
54 |
break; |
|
990 |
case EQN_TOK_EOF: |
||
991 |
555 |
return; |
|
992 |
case EQN_TOK__MAX: |
||
993 |
case EQN_TOK_FUNC: |
||
994 |
case EQN_TOK_QUOTED: |
||
995 |
case EQN_TOK_SYM: |
||
996 |
4296 |
p = ep->start; |
|
997 |
✗✓ | 4296 |
assert(p != NULL); |
998 |
/* |
||
999 |
* If we already have something in the stack and we're |
||
1000 |
* in an expression, then rewind til we're not any more. |
||
1001 |
*/ |
||
1002 |
✓✓ | 5640 |
while (parent->args == parent->expectargs) |
1003 |
672 |
parent = parent->parent; |
|
1004 |
8406 |
cur = eqn_box_alloc(ep, parent); |
|
1005 |
8406 |
cur->type = EQN_TEXT; |
|
1006 |
8406 |
cur->text = p; |
|
1007 |
✓✓✓✓ |
8406 |
switch (tok) { |
1008 |
case EQN_TOK_FUNC: |
||
1009 |
579 |
cur->font = EQNFONT_ROMAN; |
|
1010 |
579 |
break; |
|
1011 |
case EQN_TOK_QUOTED: |
||
1012 |
✓✓ | 810 |
if (cur->font == EQNFONT_NONE) |
1013 |
585 |
cur->font = EQNFONT_ITALIC; |
|
1014 |
break; |
||
1015 |
case EQN_TOK_SYM: |
||
1016 |
break; |
||
1017 |
default: |
||
1018 |
✓✓✗✓ |
5223 |
if (cur->font != EQNFONT_NONE || *p == '\0') |
1019 |
break; |
||
1020 |
2502 |
cpn = p - 1; |
|
1021 |
ccln = CCL_LET; |
||
1022 |
split = NULL; |
||
1023 |
2631 |
for (;;) { |
|
1024 |
/* Advance to next character. */ |
||
1025 |
6033 |
cp = cpn++; |
|
1026 |
ccl = ccln; |
||
1027 |
✓✓ | 16005 |
ccln = isalpha((unsigned char)*cpn) ? CCL_LET : |
1028 |
✓✓ | 7878 |
isdigit((unsigned char)*cpn) || |
1029 |
✗✓✗✗ |
3234 |
(*cpn == '.' && (ccl == CCL_DIG || |
1030 |
isdigit((unsigned char)cpn[1]))) ? |
||
1031 |
CCL_DIG : CCL_PUN; |
||
1032 |
/* No boundary before first character. */ |
||
1033 |
✓✓ | 6033 |
if (cp < p) |
1034 |
continue; |
||
1035 |
3402 |
cur->font = ccl == CCL_LET ? |
|
1036 |
EQNFONT_ITALIC : EQNFONT_ROMAN; |
||
1037 |
✓✓ | 3402 |
if (*cp == '\\') |
1038 |
126 |
mandoc_escape(&cpn, NULL, NULL); |
|
1039 |
/* No boundary after last character. */ |
||
1040 |
✓✓ | 3402 |
if (*cpn == '\0') |
1041 |
break; |
||
1042 |
✓✓✓✗ ✓✗ |
2442 |
if (ccln == ccl && *cp != ',' && *cpn != ',') |
1043 |
continue; |
||
1044 |
/* Boundary found, split the text. */ |
||
1045 |
✓✓ | 129 |
if (parent->args == parent->expectargs) { |
1046 |
/* Remove the text from the tree. */ |
||
1047 |
✓✓ | 21 |
if (cur->prev == NULL) |
1048 |
18 |
parent->first = cur->next; |
|
1049 |
else |
||
1050 |
3 |
cur->prev->next = NULL; |
|
1051 |
21 |
parent->last = cur->prev; |
|
1052 |
21 |
parent->args--; |
|
1053 |
/* Set up a list instead. */ |
||
1054 |
21 |
split = eqn_box_alloc(ep, parent); |
|
1055 |
21 |
split->type = EQN_LIST; |
|
1056 |
/* Insert the word into the list. */ |
||
1057 |
21 |
split->first = split->last = cur; |
|
1058 |
21 |
cur->parent = split; |
|
1059 |
21 |
cur->prev = NULL; |
|
1060 |
parent = split; |
||
1061 |
21 |
} |
|
1062 |
/* Append a new text box. */ |
||
1063 |
129 |
nbox = eqn_box_alloc(ep, parent); |
|
1064 |
129 |
nbox->type = EQN_TEXT; |
|
1065 |
129 |
nbox->text = mandoc_strdup(cpn); |
|
1066 |
/* Truncate the old box. */ |
||
1067 |
258 |
p = mandoc_strndup(cur->text, |
|
1068 |
129 |
cpn - cur->text); |
|
1069 |
129 |
free(cur->text); |
|
1070 |
129 |
cur->text = p; |
|
1071 |
/* Setup to process the new box. */ |
||
1072 |
cur = nbox; |
||
1073 |
129 |
p = nbox->text; |
|
1074 |
129 |
cpn = p - 1; |
|
1075 |
ccln = CCL_LET; |
||
1076 |
} |
||
1077 |
✓✓ | 2502 |
if (split != NULL) |
1078 |
21 |
parent = split->parent; |
|
1079 |
break; |
||
1080 |
} |
||
1081 |
break; |
||
1082 |
default: |
||
1083 |
abort(); |
||
1084 |
} |
||
1085 |
goto next_tok; |
||
1086 |
555 |
} |
|
1087 |
|||
1088 |
void |
||
1089 |
eqn_free(struct eqn_node *p) |
||
1090 |
{ |
||
1091 |
int i; |
||
1092 |
|||
1093 |
✓✓ | 1314 |
for (i = 0; i < (int)p->defsz; i++) { |
1094 |
72 |
free(p->defs[i].key); |
|
1095 |
72 |
free(p->defs[i].val); |
|
1096 |
} |
||
1097 |
|||
1098 |
390 |
free(p->data); |
|
1099 |
390 |
free(p->defs); |
|
1100 |
390 |
free(p); |
|
1101 |
390 |
} |
Generated by: GCOVR (Version 3.3) |