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