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